25

So I got asked today what was the best way to find the closes match within a collection.

For example, you've got an array like this:

1, 3, 8, 10, 13, ...

What number is closest to 4?

Collection is numerical, unordered and can be anything. Same with the number to match.

Lets see what we can come up with, from the various languages of choice.

animuson
  • 53,861
  • 28
  • 137
  • 147
Aaron Powell
  • 24,927
  • 18
  • 98
  • 150

33 Answers33

22

11 bytes in J:

C=:0{]/:|@-

Examples:

>> a =: 1 3 8 10 13
>> 4 C a
3
>> 11 C a
10
>> 12 C a
13

my breakdown for the layman:

0{         First element of
]          the right argument
/:         sorted by
|          absolute value 
@          of
-          subtraction
Jimmy
  • 89,068
  • 17
  • 119
  • 137
19

Shorter Python: 41 chars

f=lambda a,l:min(l,key=lambda x:abs(x-a))
Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
9

My attempt in python:

def closest(target, collection) :
    return min((abs(target - i), i) for i in collection)[1]
sykora
  • 96,888
  • 11
  • 64
  • 71
5

Groovy 28B

f={a,n->a.min{(it-n).abs()}}
matyr
  • 5,774
  • 28
  • 22
5

Some C# Linq ones... too many ways to do this!

decimal[] nums = { 1, 3, 8, 12 };
decimal target = 4;

var close1 = (from n in nums orderby Math.Abs(n-target) select n).First();
var close2 = nums.OrderBy(n => Math.Abs(n - target)).First();

Console.WriteLine("{0} and {1}", close1, close2);

Even more ways if you use a list instead, since plain ol arrays have no .Sort()

Andrew
  • 8,322
  • 2
  • 47
  • 70
4

Assuming that the values start in a table called T with a column called N, and we are looking for the value 4 then in Oracle SQL it takes 59 characters:

select*from(select*from t order by abs(n-4))where rownum=1

I've used select * to reduce the whitespace requirements.

WW.
  • 23,793
  • 13
  • 94
  • 121
  • is there no "select top 1 * from t order by abs(n-4)" type syntax in Oracle? – Jimmy Jan 16 '09 at 03:47
  • @Jimmy: Afraid not. Have to use the stupid rownum hack or analytics. Maybe a shorter version of this using analytics actually – WW. Jan 16 '09 at 05:37
4

PostgreSQL:

select n from tbl order by abs(4 - n) limit 1

In the case where two records share the same value for "abs(4 - id)" the output would be in-determinant and perhaps not a constant. To fix that I suggest something like the untested guess:

select n from tbl order by abs(4 - n) + 0.5 * 4 > n limit 1;

This solution provides performance on the order of O(N log N), where O(log N) is possible for example: https://stackoverflow.com/a/8900318/1153319

Community
  • 1
  • 1
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
4

Because I actually needed to do this, here is my PHP

$match = 33;

$set = array(1,2,3,5,8,13,21,34,55,89,144,233,377,610);

foreach ($set as $fib)
    {
        $diff[$fib] = (int) abs($match - $fib);
    }
$fibs = array_flip($diff);
$closest = $fibs[min($diff)];

echo $closest;
garrow
  • 3,459
  • 1
  • 21
  • 24
3

Ruby like Python has a min method for Enumerable so you don't need to do a sort.

def c(value, t_array)
  t_array.min{|a,b|  (value-a).abs <=> (value-b).abs }
end

ar = [1, 3, 8, 10, 13]
t = 4
c(t, ar) = 3
dgilperez
  • 10,716
  • 8
  • 68
  • 96
scottd
  • 7,364
  • 1
  • 24
  • 27
2

Python by me and https://stackoverflow.com/users/29253/igorgue based on some of the other answers here. Only 34 characters:

min([(abs(t-x), x) for x in a])[1]
Community
  • 1
  • 1
Auston
  • 480
  • 1
  • 6
  • 13
2

Language: C, Char count: 79

c(int v,int*a,int A){int n=*a;for(;--A;++a)n=abs(v-*a)<abs(v-n)?*a:n;return n;}

Signature:

int closest(int value, int *array, int array_size);

Usage:

main()
{
    int a[5] = {1, 3, 8, 10, 13};
    printf("%d\n", c(4, a, 5));
}
strager
  • 88,763
  • 26
  • 134
  • 176
  • I like this one since unlike many of the others it does not involve a sort. It is just O(n) – scottd Jul 08 '09 at 05:04
2

Scala (62 chars), based on the idea of the J and Ruby solutions:

def c(l:List[Int],n:Int)=l.sort((a,b)=>(a-n).abs<(b-n).abs)(0)

Usage:

println(c(List(1,3,8,10,13),4))
Fabian Steeg
  • 44,988
  • 7
  • 85
  • 112
2

The above code doesn't works for floating numbers.
So here's my revised php code for that.

function find_closest($match, $set=array()) {
    foreach ($set as $fib) {  
        $diff[$fib] = abs($match - $fib);  
    }  
    return array_search(min($diff), $diff);  
}

$set = array('2.3', '3.4', '3.56', '4.05', '5.5', '5.67');  
echo find_closest(3.85, $set); //return 4.05  
bigdot
  • 38
  • 7
John Laniba
  • 435
  • 3
  • 12
2

PostgreSQL:

This was pointed out by RhodiumToad on FreeNode and has performance on the order of O(log N)., much better then the other PostgreSQL answer here.

select * from ((select * from tbl where id <= 4
order by id desc limit 1) union
(select * from tbl where id >= 4
order by id limit 1)) s order by abs(4 - id) limit 1;

Both of the conditionals should be "or equal to" for much better handling of the id exists case. This also has handling in the case where two records share the same value for "abs(4 - id)" then that other PostgreSQL answer here.

Mike Mestnik
  • 313
  • 5
  • 14
1

Here's another Haskell answer:

import Control.Arrow
near4 = snd . minimum . map (abs . subtract 4 &&& id)
Drekembe
  • 2,620
  • 2
  • 16
  • 13
  • Why waste characters with "import Control.Arrow" when you can go with standard Prelude's functions - map (\x -> (abs (x-4), x)). – Martin Jonáš Jan 17 '10 at 15:56
1

Haskell, 60 characters -

f a=head.Data.List.sortBy(compare`Data.Function.on`abs.(a-))
Martin Jonáš
  • 2,309
  • 15
  • 12
1

Kdb+, 23B:

C:{x first iasc abs x-}

Usage:

q)a:10?20
q)a
12 8 10 1 9 11 5 6 1 5

q)C[a]4
5
earl
  • 40,327
  • 6
  • 58
  • 59
1

In Java Use a Navigable Map

NavigableMap <Integer, Integer>navMap = new ConcurrentSkipListMap<Integer, Integer>();  

navMap.put(15000, 3);  
navMap.put(8000, 1);  
navMap.put(12000, 2);  

System.out.println("Entry <= 12500:"+navMap.floorEntry(12500).getKey());  
System.out.println("Entry <= 12000:"+navMap.floorEntry(12000).getKey());  
System.out.println("Entry > 12000:"+navMap.higherEntry(12000).getKey()); 
stealthyninja
  • 10,343
  • 11
  • 51
  • 59
1

EDITED = in the for loop

int Closest(int val, int[] arr)
{
    int index = 0;
    for (int i = 0; i < arr.Length; i++)
        if (Math.Abs(arr[i] - val) < Math.Abs(arr[index] - val))
            index = i;
    return arr[index];
}
Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129
Al W
  • 7,539
  • 1
  • 19
  • 40
1

Haskell entry (tested):

import Data.List

near4 = head . sortBy (\n1 n2 -> abs (n1-4) `compare` abs (n2-4))

Sorts the list by putting numbers closer to 4 near the the front. head takes the first element (closest to 4).

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
1

Ruby

def c(r,t)
r.sort{|a,b|(a-t).abs<=>(b-t).abs}[0]
end

Not the most efficient method, but pretty short.

Joshua Swink
  • 3,380
  • 3
  • 29
  • 27
1

returns only one number:

var arr = new int[] { 1, 3, 8, 10, 13 };
int numToMatch = 4;
Console.WriteLine("{0}", 
     arr.Select(n => new{n, diff = Math.Abs(numToMatch - n) }).OrderBy(x => x.diff).ElementAt(0).n);
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
1

returns only one number:

var arr = new int[] { 1, 3, 8, 10, 13 };
int numToMatch = 4;

Console.WriteLine("{0}", 
   arr.OrderBy(n => Math.Abs(numToMatch - n)).ElementAt(0));
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
1

Perl -- 66 chars:

perl -e 'for(qw/1 3 8 10 13/){$d=($_-4)**2; $c=$_ if not $x or $d<$x;$x=$d;}print $c;'
gpojd
  • 22,558
  • 8
  • 42
  • 71
1

Python, not sure how to format code, and not sure if code will run as is, but it's logic should work, and there maybe builtins that do it anyways...

list = [1,4,10,20]
num = 7
for lower in list:
         if lower <= num:
           lowest = lower #closest lowest number

for higher in list:
     if higher >= num:
           highest = higher #closest highest number

if highest - num > num - lowest: # compares the differences
    closer_num = highest
else:
    closer_num = lowest
Mia Clarke
  • 8,134
  • 3
  • 49
  • 62
0

Python + numpy

import numpy as np
f = lambda lst, target: lst[np.argmin(np.abs(np.array(lst) - target))]

Example:

>>> f([1,2,5,3,10],9)
10
Joma
  • 2,334
  • 2
  • 20
  • 25
0

Ruby. One pass-through. Handles negative numbers nicely. Perhaps not very short, but certainly pretty.

class Array
  def closest int
    diff = int-self[0]; best = self[0]
    each {|i|
      if (int-i).abs < diff.abs
        best = i; diff = int-i
      end
    }
    best
  end
end

puts [1,3,8,10,13].closest 4
nilamo
  • 1,932
  • 13
  • 22
0

Excel VBA - find Excel Range's Index or Value Closest to Target Value

Given an existing Excel Range, rangeOfValues:

  1. find index of the closest matching value in the Range using Application.Match (note that the Match method returns a Double)

    Dim iMatch as Double 
    iMatch = Application.Match(valueToMatch, rangeOfValues)
    
  2. find the closest Range value to the Target Value using VLOOKUP/HLOOKUP

    Dim closest as Variant
    closest = VLOOKUP(valueToMatch, rangeOfValues)
    

If an exact match is needed:

Dim exactM as Variant
exactM = VLOOKUP(valueToMatch, rangeOfValues, False)
Himanshu
  • 31,810
  • 31
  • 111
  • 133
RichC
  • 1
  • 1
0

another PHP answer:

function closestMatch($int, $in) {
    $diffs = array();
    foreach ($in as $i)
        $diffs[abs($int - $i)] = $i;
    ksort($diffs);
    foreach ($diffs as $i) return $i;
}
kbjr
  • 1,254
  • 2
  • 10
  • 22
0
int numberToMatch = 4;

var closestMatches = new List<int>();
closestMatches.Add(arr[0]); // closest tentatively

int closestDifference = Math.Abs(numberToMatch - arr[0]);


for(int i = 1; i < arr.Length; i++)
{
    int difference = Math.Abs(numberToMatch - arr[i]);
    if (difference < closestDifference)
    {
        closestMatches.Clear();
        closestMatches.Add(arr[i]);
        closestDifference = difference;
    }
    else if (difference == closestDifference)
    {       
        closestMatches.Add(arr[i]);
    }
}


Console.WriteLine("Closest Matches");
foreach(int x in closestMatches) Console.WriteLine("{0}", x);
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
0

Some of you don't seem to be reading that the list is unordered (although with the example as it is I can understand your confusion). In Java:

public int closest(int needle, int haystack[]) { // yes i've been doing PHP lately
  assert haystack != null;
  assert haystack.length; > 0;
  int ret = haystack[0];
  int diff = Math.abs(ret - needle);
  for (int i=1; i<haystack.length; i++) {
    if (ret != haystack[i]) {
      int newdiff = Math.abs(haystack[i] - needle);
      if (newdiff < diff) {
        ret = haystack[i];
        diff = newdiff;
      }
    }
  }
  return ret;
}

Not exactly terse but hey its Java.

cletus
  • 616,129
  • 168
  • 910
  • 942
0

Common Lisp using iterate library.

(defun closest-match (list n)
     (iter (for i in list)
            (finding i minimizing (abs (- i n)))
user49117
  • 786
  • 3
  • 9
0

41 characters in F#:

let C x = Seq.min_by (fun n -> abs(n-x))

as in

#light

let l = [1;3;8;10;13]

let C x = Seq.min_by (fun n -> abs(n-x))

printfn "%d" (C 4 l)   // 3 
printfn "%d" (C 11 l)  // 10
printfn "%d" (C 12 l)  // 13
Brian
  • 117,631
  • 17
  • 236
  • 300
  • 1
    Actually, you can make it even shorter - let C x = Seq.minBy (abs<<((-)x)) ... or let C x = Seq.minBy (((-)x)>>abs) ... whichever you prefer. :) – Martin Jonáš Jan 17 '10 at 21:01