Minumum distance between two numers in Array using Javascript - javascript

I am looking for a better solution for getting minimum distance between 2 elements in an array.
Input: arr[] = {3, 5, 4, 2, 6, 5, 6, 6, 5, 4, 8, 3}, x = 3, y = 6
Output: Minimum distance between 3 and 6 is 4.
I have this Code in JS & it works fine for now.
I am looking for better code for achieving the same.
Thanks!!
<script>
var numbers= ["2", "3", "5","7","1","2","3","4","8"];
var x ="5";
var y ="8";
var firstIndex = numbers.indexOf(x);
var minD = numbers.length;
var x= numbers.forEach(function(item,index){
if((item == x) || (item == y))
{
if((index != firstIndex) && (index-firstIndex < minD))
{
minD = index-firstIndex;
firstIndex = index;
}
else
{
firstIndex = index;
}
}
});
alert(minD);
document.getElementById("demo").innerHTML = minD;
</script>

var xs=array.reduce((arr,el,i)=>(!(el===x)||arr.push(i),arr),[]);
var ys=array.reduce((arr,el,i)=>(!(el===y)||arr.push(i),arr),[]);
var lowest= xs.map(ix=>ys.map(iy=>Math.abs(iy-ix)).sort()[0]).sort()[0];
Im not sure if this is really shorter or better, just another approach...
Ive simply filtered out all x and y positions, then calculated the distance between each of them ( iy-ix) and took the smalles value (.sort()[0])
http://jsbin.com/nolohezape/edit?console

You could keep the indices and optimise the minimum value by testing if the actual difference is smaller.
function getMinDistance(array, left, right) {
var rightIndex, leftIndex, minDistance;
array.forEach(function (a, i) {
if (a === left && (leftIndex === undefined || leftIndex < i)) {
leftIndex = i;
}
if (a === right && leftIndex !== undefined) {
rightIndex = i;
}
if (leftIndex < rightIndex && (minDistance === undefined || minDistance > rightIndex - leftIndex)) {
minDistance = rightIndex - leftIndex;
}
});
return minDistance
}
console.log(getMinDistance(["2", "3", "5", "7", "1", "2", "3", "4", "8"], "5", "8"));
console.log(getMinDistance([3, 5, 4, 2, 6, 5, 6, 6, 5, 4, 8, 3], 3, 6));

function findMin(arr,a,b){
var firstIndex = arr.indexOf(a);
console.log(firstIndex);
var lastIndex = arr.indexOf(b);
console.log(lastIndex);
var minDistance;
if(firstIndex===lastIndex){
minDistance = 1;
}
if(firstIndex<lastIndex){
minDistance = lastIndex-firstIndex;
}
return minDistance;
}
console.log(findMin([1,2,3,4,5,6],1,5));

Assuming the array can be extremely large, I would rather not sort it before iterating, as it is not an efficient strategy.
The below logic scans the array from left-to-right, so in each iteration, the number marked with 👉 is checked against all proceeding numbers, until the best match is found, and then the same happens width the next number, until all possibilities have been calculated and the best (lowest) outcome is saved (minDis).
👉 - - - - - 🔥
[50, 5, 75, 66, 32, 4, 58] // diff between 50 and each number past it (min is 8)
👉 - - - 🔥 -
[50, 5, 75, 66, 32, 4, 58] // diff between 5 and each number past it (min is 1)
👉 🔥 - - -
[50, 5, 75, 66, 32, 4, 58] // diff between 75 and each number past it (min is 9)
...
On each iteration in the recursion the minDis parameter is sent to the deeper level so the local diff of that level (the for loop) is compared against that minDis argument, and if there's a smaller diff, it is then set as the "new" minDis value:
var data = [50, 5, 75, 66, 32, 4, 58]; // assume a very large array
// find the minimum distance between two numbers
function findMinDistance(arr, minDis = Infinity, idx = 0){
for( var numIdx = idx; numIdx < arr.length; numIdx++ ){
var diff = Math.abs(arr[idx] - arr[numIdx+1])
if( diff < minDis ) minDis = diff
// no need to continue scanning, since "0" is the minimum possible
if( minDis === 0 ) return 0
}
// scan from left to right, so each item is compared to the ones past it
return idx < arr.length - 1 && minDis > 0
? findMinDistance(arr, minDis, idx+1)
: minDis
}
console.log(`Min distance is: ${findMinDistance(data)}`)
A non-recursive approach, similar to the above:
var data = [50, 5, 75, 66, 32, 4, 58]; // assume a very large array
// find the minimum distance between two numbers
function findMinDistance(arr){
var minDis = Infinity, idx = 0, numIdx = idx
for( ; numIdx < arr.length; numIdx++ ){
var diff = Math.abs(arr[idx] - arr[numIdx+1])
// if result is lower than minDis, save new minDis
if( diff < minDis )
minDis = diff
// "0" is the minimum so no need to continue
if( minDis === 0 )
return 0
// go to the next number and compare it to all from its right
else if( numIdx == arr.length - 1 )
numIdx = ++idx
}
return minDis
}
console.log(`min distance is: ${findMinDistance(data)}`)

Related

Javascript Challenge: Loops - Multiple Conditions - stuck and can't figure this out

I did this module on functions and execution context - all questions have gone well but there is one challenge I have spent a lot of time on and still can't figure it out. Any help will be greatly appreciated. Thank you
Challenge question says:
Write a function addingAllTheWeirdStuff which adds the sum of all the odd numbers in array2 to each element under 10 in array1.
Similarly, addingAllTheWeirdStuff should also add the sum of all the even numbers in array2 to those elements over 10 in array1.
BONUS: If any element in array2 is greater than 20, add 1 to every element in array1.
// Uncomment these to check your work!
// console.log(addingAllTheWeirdStuff([1, 3, 5, 17, 15], [1, 2, 3, 4, 5])); // expected log [10, 12, 14, 23, 21]
// console.log(addingAllTheWeirdStuff([1, 3, 5, 17, 15, 1], [1, 2, 3, 4, 5, 22])); // expected log [11, 13, 15, 46, 44, 11]
// my attempt so far:
function addingAllTheWeirdStuff(array1, array2) {
// ADD CODE HERE
let result = []
for (let i = 0; i < array2.length; i++) {
if (array2[i] > 20) {
result = array1[i] += 1
}
}
for (let i = 0; i < array2.length; i++) {
if (array2[i] % 2 === 0 && array1[i] > 10) {
result = array1[i] + array2[i]
}
}
for (let i = 0; i < array2.length; i++) {
if (array2[i] % 2 !== 0 && array1[i] < 10) {
result = array1[i] + array2[i]
}
}
return result
}
You can easily achieve this using reduce and map array method, with the ternary operator:
const array1 = [1, 3, 5, 17, 15];
const array2 = [1, 2, 3, 4, 5];
function addingAllTheWeirdStuff(array1, array2) {
const oddSum = array2.reduce((sum, current) => current % 2 ? current + sum : 0 + sum, 0)
const oddEven = array2.reduce((sum, current) => current % 2 == 0 ? current + sum : 0 + sum, 0)
return array1.map(num => num < 10 ? num + oddSum : num + oddEven)
}
console.log(addingAllTheWeirdStuff(array1, array2))
If you break the challenge into smaller pieces, you can deconstruct it better and come up with your solutions.
This is what I did... I will be adding more comments shortly with more explanations
I chose to keep using loops as I assumed this was the point of the challenges (to practice for loops, multiple conditions, etc) - In other words, I chose to not use map / reduce on purpose but if that's allowed, use the answer by #charmful0x as it results in less code :)
// function to get sum of all odd numbers in array
function getSumOfAllOddNumbersInArray( elementArray ){
var sumOfOddNumbers = 0;
for (let i = 0; i < elementArray.length; i++) {
// use remainder operator to find out if element is odd or not
if (elementArray[i] % 2 !== 0 ) {
sumOfOddNumbers += elementArray[i];
}
}
return sumOfOddNumbers;
}
// function to get sum of all EVEN numbers in array
function getSumOfAllEvenNumbersInArray( elementArray ){
var sumOfEvenNumbers = 0;
for (let i = 0; i < elementArray.length; i++) {
// use remainder operator to find out if element is odd or not
if (elementArray[i] % 2 === 0 ) {
sumOfEvenNumbers += elementArray[i];
}
}
return sumOfEvenNumbers;
}
// Return true if there is at least one element in array that is greater than 20
function hasElementOverTwenty( elementArray ){
for (let i = 0; i < elementArray.length; i++) {
if (elementArray[i] > 20 ) {
// no need to keep looping, we found one - exit function
return true;
}
}
return false;
}
function addingAllTheWeirdStuff( firstArray, secondArray ){
var sumOfOddNumbersInArray = getSumOfAllOddNumbersInArray( secondArray );
var sumOfEvenNumbersInArray = getSumOfAllEvenNumbersInArray( secondArray );
var needToAddOne = hasElementOverTwenty( secondArray );
for (let i = 0; i < firstArray.length; i++) {
// Challenge One
if (firstArray[i] < 10) {
firstArray[i] = firstArray[i] + sumOfOddNumbersInArray;
} else if (firstArray[i] > 10) {
// Challenge Two
firstArray[i] = firstArray[i] + sumOfEvenNumbersInArray;
}
// bonus
if( needToAddOne ){
firstArray[i]++;
}
}
return firstArray;
}
// Uncomment these to check your work!
console.log(addingAllTheWeirdStuff([1, 3, 5, 17, 15], [1, 2, 3, 4, 5]));
console.log('expected:' + [10, 12, 14, 23, 21] );
console.log(addingAllTheWeirdStuff([1, 3, 5, 17, 15, 1], [1, 2, 3, 4, 5, 22]));
console.log('expected:' + [11, 13, 15, 46, 44, 11] );
Challenge question says: Write a function addingAllTheWeirdStuff which adds the sum of all the odd numbers in array2 to each element under 10 in array1.
Similarly, addingAllTheWeirdStuff should also add the sum of all the even numbers in array2 to those elements over 10 in array1.
BONUS: If any element in array2 is greater than 20, add 1 to every element in array1.

How to loop outwards from a point in an array?

I have the following array:
let arr = [ 1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15,
16, 17, 18, 19, 20,
21, 22, 23, 24, 25 ]
How will I write a function, which will loop through the elements adjacent to it. For example, if I input 13, it will go through 7,8,9,12,14,17,18 and 19 and then the elements adjacent to them i.e. 1,2,3,4,5,6,10,11,15,16,20,21,22,23 and 25.
I have tried looping separately. that is +5,-5,+6,-6,+4,-4,+1 and -1. Unfortunately, I haven't been able to make it work.
Also, if any of the inputs are corner then the rest of the elements will be looped through. For example, if 1 is given then 2,6 and 7 and then 3,8,13,12,11 and so on. In essence all elements should be looped through.
Get the size of the array and row & column of the current number. Then get all indexes from [row-1,col-1] to [row+1,col+1]. Some of the values could be negative for border elements. So, exclude those
function surronding(array, n) {
const size = Math.sqrt(array.length),
index = n - 1,
row = Math.floor(index / size),
col = index % size,
output = []
for (let x = row - 1; x <= row + 1; x++) {
for (let y = col - 1; y <= col + 1; y++) {
if (x === row && y === col) // current
continue;
if (x < 0 || y < 0 || x >= size || y >= size) // out of range
continue;
output.push(arr[x * size + y])
}
}
return output;
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
console.log(...surronding(arr, 13))
console.log(...surronding(arr, 1))
console.log(...surronding(arr, 5))
One way could be to do a bucket sort of the values, where there is a bucket for each possible distance of the value to the given "center" value, and then output the buckets in order of distance:
function solve(n, root) {
const x = --root % n;
const y = (root - x) / n;
const result = Array.from({length: n}, () => []);
for (let i = 0; i < n*n; i++) {
if (i == root) continue;
result[Math.max(Math.abs(x - i % n), Math.abs(y - Math.floor(i / n)))].push(i+1);
}
return result.flat();
}
console.log(solve(5, 13));
You could take an approach with the length of the sides and an offset for the pivot element. as result, you get an index for a linear array.
const getAdjacent = (length, startI, startJ, offset = 0) => {
const indices = [];
offset++;
for (let i = startI - offset; i <= startI + offset; i++) {
for (let j = startJ - offset; j <= startJ + offset; j++) {
if (
i < 0 || i >= length || j < 0 || j >= length ||
(i !== startI - offset && i !== startI + offset && j !== startJ - offset && j !== startJ + offset)
) continue;
indices.push(i * length + j);
}
}
return indices;
};
console.log(...getAdjacent(5, 2, 2, 0));
console.log(...getAdjacent(5, 2, 2, 1));
You first need to transpose your value to line, column position.
const arr =
[ 1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15,
16, 17, 18, 19, 20,
21, 22, 23, 24, 25 ]
function getAdjacents(number, arr)
{
let sQ = Math.sqrt(arr.length)
, n = number -1 // first position is zero
, C_ = n % sQ // column position
, R_ = (n - C_) / sQ // row position
;
return Array(3).fill().reduce((z,_,r)=>
[...z,...Array(3).fill().map((_,c)=>[r+R_-1,c+C_-1])],[])
.reduce((res,[rx,cx])=>
{
if ( rx>=0 && cx>=0
&& rx<sQ && cx<sQ
&& (rx!=R_ || cx!=C_) )
res.push((rx*sQ)+cx+1)
return res
},[])
}
console.log(13, '->', ...getAdjacents(13, arr) ) //-> 7 8 9 12 14 17 18 19
console.log( 1, '->', ...getAdjacents( 1, arr) ) //-> 2 6 7
console.log( 5, '->', ...getAdjacents( 5, arr) ) //-> 4 9 10
console.log(22, '->', ...getAdjacents(22, arr) ) //-> 16 17 18 21 23
.as-console-wrapper {max-height: 100%!important;top:0;}
I'm guessing that this fundamentally works the same way as #trincot's answer but I already had written most of it, hopefully it's okay to still post it.
const row_col_for_number = (square_side_size, number) => [
parseInt((number - 1) / square_side_size) + 1,
((number - 1) % square_side_size) + 1
];
const biggest_row_col_diff = ([a_row, a_col], [b_row, b_col]) =>
Math.max(Math.abs(a_row - b_row), Math.abs(a_col - b_col));
const distance_to_start = (square_side_size, start_row_col, number) =>
biggest_row_col_diff(
start_row_col,
row_col_for_number(square_side_size, number)
);
const sort_square_adjacency = (square_side_size, start_row_col) => (a, b) =>
distance_to_start(square_side_size, start_row_col, a) -
distance_to_start(square_side_size, start_row_col, b);
const doit = (square_side_size, start_number) =>
new Array(square_side_size * square_side_size)
.fill(0)
.map((_, i) => i + 1)
.sort(
sort_square_adjacency(
square_side_size,
row_col_for_number(square_side_size, start_number)
)
)
.slice(1);
console.log(...doit(5, 1));
console.log(...doit(5, 13));

Finding the closest level by experience [duplicate]

I have a number from minus 1000 to plus 1000 and I have an array with numbers in it. Like this:
[2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
I want that the number I've got changes to the nearest number of the array.
For example I get 80 as number I want it to get 82.
ES5 Version:
var counts = [4, 9, 15, 6, 2],
goal = 5;
var closest = counts.reduce(function(prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
console.log(closest);
Here's the pseudo-code which should be convertible into any procedural language:
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 112
print closest (number, array)
def closest (num, arr):
curr = arr[0]
foreach val in arr:
if abs (num - val) < abs (num - curr):
curr = val
return curr
It simply works out the absolute differences between the given number and each array element and gives you back one of the ones with the minimal difference.
For the example values:
number = 112 112 112 112 112 112 112 112 112 112
array = 2 42 82 122 162 202 242 282 322 362
diff = 110 70 30 10 50 90 130 170 210 250
|
+-- one with minimal absolute difference.
As a proof of concept, here's the Python code I used to show this in action:
def closest (num, arr):
curr = arr[0]
for index in range (len (arr)):
if abs (num - arr[index]) < abs (num - curr):
curr = arr[index]
return curr
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 112
print closest (number, array)
And, if you really need it in Javascript, see below for a complete HTML file which demonstrates the function in action:
<html>
<head></head>
<body>
<script language="javascript">
function closest (num, arr) {
var curr = arr[0];
var diff = Math.abs (num - curr);
for (var val = 0; val < arr.length; val++) {
var newdiff = Math.abs (num - arr[val]);
if (newdiff < diff) {
diff = newdiff;
curr = arr[val];
}
}
return curr;
}
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
number = 112;
alert (closest (number, array));
</script>
</body>
</html>
Now keep in mind there may be scope for improved efficiency if, for example, your data items are sorted (that could be inferred from the sample data but you don't explicitly state it). You could, for example, use a binary search to find the closest item.
You should also keep in mind that, unless you need to do it many times per second, the efficiency improvements will be mostly unnoticable unless your data sets get much larger.
If you do want to try it that way (and can guarantee the array is sorted in ascending order), this is a good starting point:
<html>
<head></head>
<body>
<script language="javascript">
function closest (num, arr) {
var mid;
var lo = 0;
var hi = arr.length - 1;
while (hi - lo > 1) {
mid = Math.floor ((lo + hi) / 2);
if (arr[mid] < num) {
lo = mid;
} else {
hi = mid;
}
}
if (num - arr[lo] <= arr[hi] - num) {
return arr[lo];
}
return arr[hi];
}
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
number = 112;
alert (closest (number, array));
</script>
</body>
</html>
It basically uses bracketing and checking of the middle value to reduce the solution space by half for each iteration, a classic O(log N) algorithm whereas the sequential search above was O(N):
0 1 2 3 4 5 6 7 8 9 <- indexes
2 42 82 122 162 202 242 282 322 362 <- values
L M H L=0, H=9, M=4, 162 higher, H<-M
L M H L=0, H=4, M=2, 82 lower/equal, L<-M
L M H L=2, H=4, M=3, 122 higher, H<-M
L H L=2, H=3, difference of 1 so exit
^
|
H (122-112=10) is closer than L (112-82=30) so choose H
As stated, that shouldn't make much of a difference for small datasets or for things that don't need to be blindingly fast, but it's an option you may want to consider.
ES6 (ECMAScript 2015) Version:
const counts = [4, 9, 15, 6, 2];
const goal = 5;
const output = counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
console.log(output);
For reusability you can wrap in a curry function that supports placeholders (http://ramdajs.com/0.19.1/docs/#curry or https://lodash.com/docs#curry). This gives lots of flexibility depending on what you need:
const getClosest = _.curry((counts, goal) => {
return counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
const closestToFive = getClosest(_, 5);
const output = closestToFive([4, 9, 15, 6, 2]);
console.log(output);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
Working code as below:
var array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
function closest(array, num) {
var i = 0;
var minDiff = 1000;
var ans;
for (i in array) {
var m = Math.abs(num - array[i]);
if (m < minDiff) {
minDiff = m;
ans = array[i];
}
}
return ans;
}
console.log(closest(array, 88));
Works with unsorted arrays
While there were some good solutions posted here, JavaScript is a flexible language that gives us tools to solve a problem in many different ways.
It all comes down to your style, of course. If your code is more functional, you'll find the reduce variation suitable, i.e.:
arr.reduce(function (prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
However, some might find that hard to read, depending on their coding style. Therefore I propose a new way of solving the problem:
var findClosest = function (x, arr) {
var indexArr = arr.map(function(k) { return Math.abs(k - x) })
var min = Math.min.apply(Math, indexArr)
return arr[indexArr.indexOf(min)]
}
findClosest(80, [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]) // Outputs 82
Contrary to other approaches finding the minimum value using Math.min.apply, this one doesn't require the input array arr to be sorted. We don't need to care about the indexes or sort it beforehand.
I'll explain the code line by line for clarity:
arr.map(function(k) { return Math.abs(k - x) }) Creates a new array, essentially storing the absolute values of the given numbers (number in arr) minus the input number (x). We'll look for the smallest number next (which is also the closest to the input number)
Math.min.apply(Math, indexArr) This is a legit way of finding the smallest number in the array we've just created before (nothing more to it)
arr[indexArr.indexOf(min)] This is perhaps the most interesting part. We have found our smallest number, but we're not sure if we should add or subtract the initial number (x). That's because we used Math.abs() to find the difference. However, array.map creates (logically) a map of the input array, keeping the indexes in the same place. Therefore, to find out the closest number we just return the index of the found minimum in the given array indexArr.indexOf(min).
I've created a bin demonstrating it.
All of the solutions are over-engineered.
It is as simple as:
const needle = 5;
const haystack = [1, 2, 3, 4, 5, 6, 7, 8, 9];
haystack.sort((a, b) => {
return Math.abs(a - needle) - Math.abs(b - needle);
})[0];
// 5
For sorted arrays (linear search)
All answers so far concentrate on searching through the whole array.
Considering your array is sorted already and you really only want the nearest number this is probably the easiest (but not fastest) solution:
var a = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
var target = 90000;
/**
* Returns the closest number from a sorted array.
**/
function closest(arr, target) {
if (!(arr) || arr.length == 0)
return null;
if (arr.length == 1)
return arr[0];
for (var i = 1; i < arr.length; i++) {
// As soon as a number bigger than target is found, return the previous or current
// number depending on which has smaller difference to the target.
if (arr[i] > target) {
var p = arr[i - 1];
var c = arr[i]
return Math.abs(p - target) < Math.abs(c - target) ? p : c;
}
}
// No number in array is bigger so return the last.
return arr[arr.length - 1];
}
// Trying it out
console.log(closest(a, target));
Note that the algorithm can be vastly improved e.g. using a binary tree.
ES6
Works with sorted and unsorted arrays
Numbers Integers and Floats, Strings welcomed
/**
* Finds the nearest value in an array of numbers.
* Example: nearestValue(array, 42)
*
* #param {Array<number>} arr
* #param {number} val the ideal value for which the nearest or equal should be found
*/
const nearestValue = (arr, val) => arr.reduce((p, n) => (Math.abs(p) > Math.abs(n - val) ? n - val : p), Infinity) + val
Examples:
let values = [1,2,3,4,5]
console.log(nearestValue(values, 10)) // --> 5
console.log(nearestValue(values, 0)) // --> 1
console.log(nearestValue(values, 2.5)) // --> 2
values = [100,5,90,56]
console.log(nearestValue(values, 42)) // --> 56
values = ['100','5','90','56']
console.log(nearestValue(values, 42)) // --> 56
This solution uses ES5 existential quantifier Array#some, which allows to stop the iteration, if a condition is met.
Opposit of Array#reduce, it does not need to iterate all elements for one result.
Inside the callback, an absolute delta between the searched value and actual item is taken and compared with the last delta. If greater or equal, the iteration stops, because all other values with their deltas are greater than the actual value.
If the delta in the callback is smaller, then the actual item is assigned to the result and the delta is saved in lastDelta.
Finally, smaller values with equal deltas are taken, like in the below example of 22, which results in 2.
If there is a priority of greater values, the delta check has to be changed from:
if (delta >= lastDelta) {
to:
if (delta > lastDelta) {
// ^^^ without equal sign
This would get with 22, the result 42 (Priority of greater values).
This function needs sorted values in the array.
Code with priority of smaller values:
function closestValue(array, value) {
var result,
lastDelta;
array.some(function (item) {
var delta = Math.abs(value - item);
if (delta >= lastDelta) {
return true;
}
result = item;
lastDelta = delta;
});
return result;
}
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log(21, closestValue(data, 21)); // 2
console.log(22, closestValue(data, 22)); // 2 smaller value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82
Code with priority of greater values:
function closestValue(array, value) {
var result,
lastDelta;
array.some(function (item) {
var delta = Math.abs(value - item);
if (delta > lastDelta) {
return true;
}
result = item;
lastDelta = delta;
});
return result;
}
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log(21, closestValue(data, 21)); // 2
console.log(22, closestValue(data, 22)); // 42 greater value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82
Other answers suggested the you would need to iterate through the entire array:
calculate the deviation for each element
keep track of the smallest deviation and its element
finally, after iterating through the entire array, return that element with that smallest deviation.
If the array is already sorted, that does not make sense. There is no need to calculate all deviations. e.g. in an ordered collection of 1 million elements, you only need to calculate ~19 deviations (at most) to find your match. You can accomplish this with a binary-search approach:
function findClosestIndex(arr, element) {
let from = 0, until = arr.length - 1
while (true) {
const cursor = Math.floor((from + until) / 2);
if (cursor === from) {
const diff1 = element - arr[from];
const diff2 = arr[until] - element;
return diff1 <= diff2 ? from : until;
}
const found = arr[cursor];
if (found === element) return cursor;
if (found > element) {
until = cursor;
} else if (found < element) {
from = cursor;
}
}
}
Result:
console.log(findClosestIndex([0, 1, 2, 3.5, 4.5, 5], 4));
// output: 3
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 4));
// output: 4
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 90));
// output: 5
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], -1));
// output: 0
A simpler way with O(n) time complexity is to do this in one iteration of the array. This method is intended for unsorted arrays.
Following is a javascript example, here from the array we find the number which is nearest to "58".
var inputArr = [150, 5, 200, 50, 30];
var search = 58;
var min = Math.min();
var result = 0;
for(i=0;i<inputArr.length;i++) {
let absVal = Math.abs(search - inputArr[i])
if(min > absVal) {
min=absVal;
result = inputArr[i];
}
}
console.log(result); //expected output 50 if input is 58
This will work for positive, negative, decimal numbers as well.
Math.min() will return Infinity.
The result will store the value nearest to the search element.
I don't know if I'm supposed to answer an old question, but as this post appears first on Google searches, I hoped that you would forgive me adding my solution & my 2c here.
Being lazy, I couldn't believe that the solution for this question would be a LOOP, so I searched a bit more and came back with filter function:
var myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
var myValue = 80;
function BiggerThan(inArray) {
return inArray > myValue;
}
var arrBiggerElements = myArray.filter(BiggerThan);
var nextElement = Math.min.apply(null, arrBiggerElements);
alert(nextElement);
That's all !
My answer to a similar question is accounting for ties too and it is in plain Javascript, although it doesn't use binary search so it is O(N) and not O(logN):
var searchArray= [0, 30, 60, 90];
var element= 33;
function findClosest(array,elem){
var minDelta = null;
var minIndex = null;
for (var i = 0 ; i<array.length; i++){
var delta = Math.abs(array[i]-elem);
if (minDelta == null || delta < minDelta){
minDelta = delta;
minIndex = i;
}
//if it is a tie return an array of both values
else if (delta == minDelta) {
return [array[minIndex],array[i]];
}//if it has already found the closest value
else {
return array[i-1];
}
}
return array[minIndex];
}
var closest = findClosest(searchArray,element);
https://stackoverflow.com/a/26429528/986160
I like the approach from Fusion, but there's a small error in it. Like that it is correct:
function closest(array, number) {
var num = 0;
for (var i = array.length - 1; i >= 0; i--) {
if(Math.abs(number - array[i]) < Math.abs(number - array[num])){
num = i;
}
}
return array[num];
}
It it also a bit faster because it uses the improved for loop.
At the end I wrote my function like this:
var getClosest = function(number, array) {
var current = array[0];
var difference = Math.abs(number - current);
var index = array.length;
while (index--) {
var newDifference = Math.abs(number - array[index]);
if (newDifference < difference) {
difference = newDifference;
current = array[index];
}
}
return current;
};
I tested it with console.time() and it is slightly faster than the other function.
The most efficient would be a binary search. However even simple solutions can exit when the next number is a further match from the current. Nearly all solutions here are not taking into account that the array is ordered and iterating though the whole thing :/
const closest = (orderedArray, value, valueGetter = item => item) =>
orderedArray.find((item, i) =>
i === orderedArray.length - 1 ||
Math.abs(value - valueGetter(item)) < Math.abs(value - valueGetter(orderedArray[i + 1])));
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log('21 -> 2', closest(data, 21) === 2);
console.log('22 -> 42', closest(data, 22) === 42); // equidistant between 2 and 42, select highest
console.log('23 -> 42', closest(data, 23) === 42);
console.log('80 -> 82', closest(data, 80) === 82);
This can be run on non-primitives too e.g. closest(data, 21, item => item.age)
Change find to findIndex to return the index in the array.
If the array is sorted like in your example, you can use a Binary Search for a better time complexity of O(log n).
const myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
const binaryClosestIdx = (arr, target) => {
let start = 0;
let end = arr.length - 1;
let mid = Math.floor((start + end) / 2);
while (1) {
if (arr[mid] === target) {
return mid;
}
else if (start >= end) {
break;
}
else if (arr[mid] > target) {
end = mid - 1;
} else {
start = mid + 1;
}
mid = Math.floor((start + end) / 2);
}
// Return the closest between the last value checked and it's surrounding neighbors
const first = Math.max(mid - 1, 0);
const neighbors = arr.slice(first, mid + 2);
const best = neighbors.reduce((b, el) => Math.abs(el - target) < Math.abs(b - target) ? el : b);
return first + neighbors.indexOf(best);
}
const closestValue = myArray[binaryClosestIdx(myArray, 80)];
console.log(closestValue);
How it works :
It compares the target value to the middle element of the array. If the middle element is bigger we can ignore every element after it as they are going to be even bigger. The same goes if the middle element is smaller, we can ignore every element before it.
If the target value is found we return it, otherwise we compare the last value tested with its surrounding neighbors as the closest value can only be between those 3 values.
Another variant here we have circular range connecting head to toe and accepts only min value to given input. This had helped me get char code values for one of the encryption algorithm.
function closestNumberInCircularRange(codes, charCode) {
return codes.reduce((p_code, c_code)=>{
if(((Math.abs(p_code-charCode) > Math.abs(c_code-charCode)) || p_code > charCode) && c_code < charCode){
return c_code;
}else if(p_code < charCode){
return p_code;
}else if(p_code > charCode && c_code > charCode){
return Math.max.apply(Math, [p_code, c_code]);
}
return p_code;
});
}
To Find Two Closest Number in array
function findTwoClosest(givenList, goal) {
var first;
var second;
var finalCollection = [givenList[0], givenList[1]];
givenList.forEach((item, firtIndex) => {
first = item;
for (let i = firtIndex + 1; i < givenList.length; i++) {
second = givenList[i];
if (first + second < goal) {
if (first + second > finalCollection[0] + finalCollection[1]) {
finalCollection = [first, second];
}
}
}
});
return finalCollection;
}
var counts = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
var goal = 80;
console.log(findTwoClosest(counts, goal));
You can use below logic to find closest number without using reduce function
let arr = [0, 80, 10, 60, 20, 50, 0, 100, 80, 70, 1];
const n = 2;
let closest = -1;
let closeDiff = -1;
for (let i = 0; i < arr.length; i++) {
if (Math.abs(arr[i] - n) < closeDiff || closest === -1) {
closeDiff = Math.abs(arr[i] - n);
closest = arr[i];
}
}
console.log(closest);
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
class CompareFunctor
{
public:
CompareFunctor(int n) { _n = n; }
bool operator()(int & val1, int & val2)
{
int diff1 = abs(val1 - _n);
int diff2 = abs(val2 - _n);
return (diff1 < diff2);
}
private:
int _n;
};
int Find_Closest_Value(int nums[], int size, int n)
{
CompareFunctor cf(n);
int cn = *min_element(nums, nums + size, cf);
return cn;
}
int main()
{
int nums[] = { 2, 42, 82, 122, 162, 202, 242, 282, 322, 362 };
int size = sizeof(nums) / sizeof(int);
int n = 80;
int cn = Find_Closest_Value(nums, size, n);
cout << "\nClosest value = " << cn << endl;
cin.get();
}
For a small range, the simplest thing is to have a map array, where, eg, the 80th entry would have the value 82 in it, to use your example. For a much larger, sparse range, probably the way to go is a binary search.
With a query language you could query for values some distance either side of your input number and then sort through the resulting reduced list. But SQL doesn't have a good concept of "next" or "previous", to give you a "clean" solution.

Find the diagonals in an array that is a representation of a 2d array

I converted my 2d array to a 1d array. For example: (Starts at 0, not 1);
00 01 02 03 04
05 06 07 08 09
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24
was converted to a 1d array.
[0, 1, 2, 3, 4.....23, 24].
I am now attempting to create a function that finds every spot that is "connected" or next to a certain element in the array. This includes elements that are diagonal from it. So using the above 2d array, if I want an array of elements that are connected to 0, I expect the function to return the array
[1, 5, 6].
The trouble I am having is finding the diagonals. This is my JS code for the array that should be returned.
var poss = [Number(num+1),Number(num-1),Number(num+col),Number(num-col),Number((num+col) + 1),Number((num+col) - 1),Number((num-col) + 1),Number((num-col) - 1)];
This returns [1, 5, 6, 4].
I have code that excludes negative numbers. However, 4 should not be there.
I realize this is because this is an edge case and it isn't registering as out of bounds because it isn't a negative number. Is there a formula of some sort that will find the elements connected to it diagonally ? Remember I am using a 1d array. This program also run independently of the array size. So this will also have to work for boards that are 4x4 or 5x4. So using the row and num fields is ideal.
I've seemed to have figured it out. At the very least, I've passed all of my test cases. I am sure that this is not the most simply, elegant, or efficient way. More test cases are probably needed. I made sure it is calculated independent of the number of columns and rows. Here is the huge if statement I used.
var x = poss[i]
if((Number(num) % col == 0 && Number(num-1) == Number(x)) ||
(Number(num+1) % col == 0 && Number(num+1) == Number(x)) ||
((Number(num) % col == 0 || Number(num+1) % col == 0) && (Number((num-col) -1) == Number(x))) ||
((Number(num) % col == 0 || Number(num+1) % col == 0) && (Number((num+col) -1) == Number(x))) ||
((Number(num) % col == 0 || Number(num+1) % col == 0) && (Number((num-col) +1) == Number(x))) ||
((Number(num) % col == 0 || Number(num+1) % col == 0) && (Number((num+col) +1) == Number(x))))
{//exclude number from results}
The variable num is the number on the array that you are currently searching neighbors for. Variable x is one of the possible neighbors.
Feel free to post another idea.
This was my solution to this issue, it should be easy to read and understand and I added some comments on it as well.
var cols = 5;
var rows = 5;
function connectedPoints(point) {
var connectedPoints = [];
// First test if the point is on an edge
var topEdge = point/cols < 1;
var leftEdge = point%cols == 0;
var rightEdge = point%cols == cols-1;
var bottomEdge = point/cols >= rows-1;
// Add points that are above the point
if (!topEdge) {
if (!leftEdge) {
connectedPoints.push(returnIfNotNegative(point-cols-1));
}
connectedPoints.push(returnIfNotNegative(point-cols));
if (!rightEdge) {
connectedPoints.push(returnIfNotNegative(point-cols+1));
}
}
// Add points that are to the left or right of the point
if (!leftEdge) {
connectedPoints.push(returnIfNotNegative(point-1));
}
if (!rightEdge) {
connectedPoints.push(returnIfNotNegative(point+1));
}
// Add points that are below the point
if (!bottomEdge) {
if (!leftEdge) {
connectedPoints.push(returnIfNotNegative(point+cols-1));
}
connectedPoints.push(returnIfNotNegative(point+cols));
if (!rightEdge) {
connectedPoints.push(returnIfNotNegative(point+cols+1));
}
}
console.log(connectedPoints);
}
function returnIfNotNegative(point) {
if (point < 0) {
return null;
}
return point;
}
connectedPoints(0);
You can try using position instead of value. This will simplify things.
You can have another function to get position of value.
var arr = [
[00, 01, 02, 03, 04],
[05, 06, 07, 08, 09],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]
]
function getNeighbours(x, y) {
var result = [];
for (var i = x - 1; i <= x + 1; i++) {
for (var j = y - 1; j <= y + 1; j++) {
if (arr[i] && arr[i][j]) {
if (!(x === i && y === j))
result.push(arr[i][j]);
}
}
}
return result;
}
console.log(getNeighbours(0, 0));
console.log(getNeighbours(3, 3));
When working with the arrays it's important to move as much computation outside the inner loop as possible. My approach was to find the start positions of the left column in each row and then iterating over the column range.
var arr = [
0, 1, 2, 3, 4,
5, 6, 7, 8, 9,
10, 11, 12, 13, 14,
15, 16, 17, 18, 19,
20, 21, 22, 23, 24
]
function getNeighbours(col, row)
{
var w = 5;
var start = (row > 0 ? row * w - w : row * w) + (col > 0 ? col - 1 : col);
var rowSpan = ((row > 0 ? 2 : 1) + (row < (w - 1) ? 1 : 0)) * w;
var colSpan = (col > 0 ? 2 : 1) + (col < (w -1) ? 1 : 0);
var center = col + row * w;
var result = [];
for (var r = start; r < start + rowSpan; r += w)
for (var i = r; i < r + colSpan; i++)
if (!(i === center))
result.push(arr[i]);
return result;
}
console.log(getNeighbours(0,0));
console.log(getNeighbours(3,3));
console.log(getNeighbours(3,4));
console.log(getNeighbours(4,3));
console.log(getNeighbours(4,4));
/*
[1, 5, 6]
[12, 13, 14, 17, 19, 22, 23, 24]
[17, 18, 19, 22, 24]
[13, 14, 18, 23, 24]
[18, 19, 23]
*/

Get the closest number out of an array

I have a number from minus 1000 to plus 1000 and I have an array with numbers in it. Like this:
[2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
I want that the number I've got changes to the nearest number of the array.
For example I get 80 as number I want it to get 82.
ES5 Version:
var counts = [4, 9, 15, 6, 2],
goal = 5;
var closest = counts.reduce(function(prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
console.log(closest);
Here's the pseudo-code which should be convertible into any procedural language:
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 112
print closest (number, array)
def closest (num, arr):
curr = arr[0]
foreach val in arr:
if abs (num - val) < abs (num - curr):
curr = val
return curr
It simply works out the absolute differences between the given number and each array element and gives you back one of the ones with the minimal difference.
For the example values:
number = 112 112 112 112 112 112 112 112 112 112
array = 2 42 82 122 162 202 242 282 322 362
diff = 110 70 30 10 50 90 130 170 210 250
|
+-- one with minimal absolute difference.
As a proof of concept, here's the Python code I used to show this in action:
def closest (num, arr):
curr = arr[0]
for index in range (len (arr)):
if abs (num - arr[index]) < abs (num - curr):
curr = arr[index]
return curr
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 112
print closest (number, array)
And, if you really need it in Javascript, see below for a complete HTML file which demonstrates the function in action:
<html>
<head></head>
<body>
<script language="javascript">
function closest (num, arr) {
var curr = arr[0];
var diff = Math.abs (num - curr);
for (var val = 0; val < arr.length; val++) {
var newdiff = Math.abs (num - arr[val]);
if (newdiff < diff) {
diff = newdiff;
curr = arr[val];
}
}
return curr;
}
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
number = 112;
alert (closest (number, array));
</script>
</body>
</html>
Now keep in mind there may be scope for improved efficiency if, for example, your data items are sorted (that could be inferred from the sample data but you don't explicitly state it). You could, for example, use a binary search to find the closest item.
You should also keep in mind that, unless you need to do it many times per second, the efficiency improvements will be mostly unnoticable unless your data sets get much larger.
If you do want to try it that way (and can guarantee the array is sorted in ascending order), this is a good starting point:
<html>
<head></head>
<body>
<script language="javascript">
function closest (num, arr) {
var mid;
var lo = 0;
var hi = arr.length - 1;
while (hi - lo > 1) {
mid = Math.floor ((lo + hi) / 2);
if (arr[mid] < num) {
lo = mid;
} else {
hi = mid;
}
}
if (num - arr[lo] <= arr[hi] - num) {
return arr[lo];
}
return arr[hi];
}
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
number = 112;
alert (closest (number, array));
</script>
</body>
</html>
It basically uses bracketing and checking of the middle value to reduce the solution space by half for each iteration, a classic O(log N) algorithm whereas the sequential search above was O(N):
0 1 2 3 4 5 6 7 8 9 <- indexes
2 42 82 122 162 202 242 282 322 362 <- values
L M H L=0, H=9, M=4, 162 higher, H<-M
L M H L=0, H=4, M=2, 82 lower/equal, L<-M
L M H L=2, H=4, M=3, 122 higher, H<-M
L H L=2, H=3, difference of 1 so exit
^
|
H (122-112=10) is closer than L (112-82=30) so choose H
As stated, that shouldn't make much of a difference for small datasets or for things that don't need to be blindingly fast, but it's an option you may want to consider.
ES6 (ECMAScript 2015) Version:
const counts = [4, 9, 15, 6, 2];
const goal = 5;
const output = counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
console.log(output);
For reusability you can wrap in a curry function that supports placeholders (http://ramdajs.com/0.19.1/docs/#curry or https://lodash.com/docs#curry). This gives lots of flexibility depending on what you need:
const getClosest = _.curry((counts, goal) => {
return counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
const closestToFive = getClosest(_, 5);
const output = closestToFive([4, 9, 15, 6, 2]);
console.log(output);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
Working code as below:
var array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
function closest(array, num) {
var i = 0;
var minDiff = 1000;
var ans;
for (i in array) {
var m = Math.abs(num - array[i]);
if (m < minDiff) {
minDiff = m;
ans = array[i];
}
}
return ans;
}
console.log(closest(array, 88));
Works with unsorted arrays
While there were some good solutions posted here, JavaScript is a flexible language that gives us tools to solve a problem in many different ways.
It all comes down to your style, of course. If your code is more functional, you'll find the reduce variation suitable, i.e.:
arr.reduce(function (prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
However, some might find that hard to read, depending on their coding style. Therefore I propose a new way of solving the problem:
var findClosest = function (x, arr) {
var indexArr = arr.map(function(k) { return Math.abs(k - x) })
var min = Math.min.apply(Math, indexArr)
return arr[indexArr.indexOf(min)]
}
findClosest(80, [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]) // Outputs 82
Contrary to other approaches finding the minimum value using Math.min.apply, this one doesn't require the input array arr to be sorted. We don't need to care about the indexes or sort it beforehand.
I'll explain the code line by line for clarity:
arr.map(function(k) { return Math.abs(k - x) }) Creates a new array, essentially storing the absolute values of the given numbers (number in arr) minus the input number (x). We'll look for the smallest number next (which is also the closest to the input number)
Math.min.apply(Math, indexArr) This is a legit way of finding the smallest number in the array we've just created before (nothing more to it)
arr[indexArr.indexOf(min)] This is perhaps the most interesting part. We have found our smallest number, but we're not sure if we should add or subtract the initial number (x). That's because we used Math.abs() to find the difference. However, array.map creates (logically) a map of the input array, keeping the indexes in the same place. Therefore, to find out the closest number we just return the index of the found minimum in the given array indexArr.indexOf(min).
I've created a bin demonstrating it.
All of the solutions are over-engineered.
It is as simple as:
const needle = 5;
const haystack = [1, 2, 3, 4, 5, 6, 7, 8, 9];
haystack.sort((a, b) => {
return Math.abs(a - needle) - Math.abs(b - needle);
})[0];
// 5
For sorted arrays (linear search)
All answers so far concentrate on searching through the whole array.
Considering your array is sorted already and you really only want the nearest number this is probably the easiest (but not fastest) solution:
var a = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
var target = 90000;
/**
* Returns the closest number from a sorted array.
**/
function closest(arr, target) {
if (!(arr) || arr.length == 0)
return null;
if (arr.length == 1)
return arr[0];
for (var i = 1; i < arr.length; i++) {
// As soon as a number bigger than target is found, return the previous or current
// number depending on which has smaller difference to the target.
if (arr[i] > target) {
var p = arr[i - 1];
var c = arr[i]
return Math.abs(p - target) < Math.abs(c - target) ? p : c;
}
}
// No number in array is bigger so return the last.
return arr[arr.length - 1];
}
// Trying it out
console.log(closest(a, target));
Note that the algorithm can be vastly improved e.g. using a binary tree.
ES6
Works with sorted and unsorted arrays
Numbers Integers and Floats, Strings welcomed
/**
* Finds the nearest value in an array of numbers.
* Example: nearestValue(array, 42)
*
* #param {Array<number>} arr
* #param {number} val the ideal value for which the nearest or equal should be found
*/
const nearestValue = (arr, val) => arr.reduce((p, n) => (Math.abs(p) > Math.abs(n - val) ? n - val : p), Infinity) + val
Examples:
let values = [1,2,3,4,5]
console.log(nearestValue(values, 10)) // --> 5
console.log(nearestValue(values, 0)) // --> 1
console.log(nearestValue(values, 2.5)) // --> 2
values = [100,5,90,56]
console.log(nearestValue(values, 42)) // --> 56
values = ['100','5','90','56']
console.log(nearestValue(values, 42)) // --> 56
This solution uses ES5 existential quantifier Array#some, which allows to stop the iteration, if a condition is met.
Opposit of Array#reduce, it does not need to iterate all elements for one result.
Inside the callback, an absolute delta between the searched value and actual item is taken and compared with the last delta. If greater or equal, the iteration stops, because all other values with their deltas are greater than the actual value.
If the delta in the callback is smaller, then the actual item is assigned to the result and the delta is saved in lastDelta.
Finally, smaller values with equal deltas are taken, like in the below example of 22, which results in 2.
If there is a priority of greater values, the delta check has to be changed from:
if (delta >= lastDelta) {
to:
if (delta > lastDelta) {
// ^^^ without equal sign
This would get with 22, the result 42 (Priority of greater values).
This function needs sorted values in the array.
Code with priority of smaller values:
function closestValue(array, value) {
var result,
lastDelta;
array.some(function (item) {
var delta = Math.abs(value - item);
if (delta >= lastDelta) {
return true;
}
result = item;
lastDelta = delta;
});
return result;
}
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log(21, closestValue(data, 21)); // 2
console.log(22, closestValue(data, 22)); // 2 smaller value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82
Code with priority of greater values:
function closestValue(array, value) {
var result,
lastDelta;
array.some(function (item) {
var delta = Math.abs(value - item);
if (delta > lastDelta) {
return true;
}
result = item;
lastDelta = delta;
});
return result;
}
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log(21, closestValue(data, 21)); // 2
console.log(22, closestValue(data, 22)); // 42 greater value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82
Other answers suggested the you would need to iterate through the entire array:
calculate the deviation for each element
keep track of the smallest deviation and its element
finally, after iterating through the entire array, return that element with that smallest deviation.
If the array is already sorted, that does not make sense. There is no need to calculate all deviations. e.g. in an ordered collection of 1 million elements, you only need to calculate ~19 deviations (at most) to find your match. You can accomplish this with a binary-search approach:
function findClosestIndex(arr, element) {
let from = 0, until = arr.length - 1
while (true) {
const cursor = Math.floor((from + until) / 2);
if (cursor === from) {
const diff1 = element - arr[from];
const diff2 = arr[until] - element;
return diff1 <= diff2 ? from : until;
}
const found = arr[cursor];
if (found === element) return cursor;
if (found > element) {
until = cursor;
} else if (found < element) {
from = cursor;
}
}
}
Result:
console.log(findClosestIndex([0, 1, 2, 3.5, 4.5, 5], 4));
// output: 3
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 4));
// output: 4
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 90));
// output: 5
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], -1));
// output: 0
A simpler way with O(n) time complexity is to do this in one iteration of the array. This method is intended for unsorted arrays.
Following is a javascript example, here from the array we find the number which is nearest to "58".
var inputArr = [150, 5, 200, 50, 30];
var search = 58;
var min = Math.min();
var result = 0;
for(i=0;i<inputArr.length;i++) {
let absVal = Math.abs(search - inputArr[i])
if(min > absVal) {
min=absVal;
result = inputArr[i];
}
}
console.log(result); //expected output 50 if input is 58
This will work for positive, negative, decimal numbers as well.
Math.min() will return Infinity.
The result will store the value nearest to the search element.
I don't know if I'm supposed to answer an old question, but as this post appears first on Google searches, I hoped that you would forgive me adding my solution & my 2c here.
Being lazy, I couldn't believe that the solution for this question would be a LOOP, so I searched a bit more and came back with filter function:
var myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
var myValue = 80;
function BiggerThan(inArray) {
return inArray > myValue;
}
var arrBiggerElements = myArray.filter(BiggerThan);
var nextElement = Math.min.apply(null, arrBiggerElements);
alert(nextElement);
That's all !
My answer to a similar question is accounting for ties too and it is in plain Javascript, although it doesn't use binary search so it is O(N) and not O(logN):
var searchArray= [0, 30, 60, 90];
var element= 33;
function findClosest(array,elem){
var minDelta = null;
var minIndex = null;
for (var i = 0 ; i<array.length; i++){
var delta = Math.abs(array[i]-elem);
if (minDelta == null || delta < minDelta){
minDelta = delta;
minIndex = i;
}
//if it is a tie return an array of both values
else if (delta == minDelta) {
return [array[minIndex],array[i]];
}//if it has already found the closest value
else {
return array[i-1];
}
}
return array[minIndex];
}
var closest = findClosest(searchArray,element);
https://stackoverflow.com/a/26429528/986160
I like the approach from Fusion, but there's a small error in it. Like that it is correct:
function closest(array, number) {
var num = 0;
for (var i = array.length - 1; i >= 0; i--) {
if(Math.abs(number - array[i]) < Math.abs(number - array[num])){
num = i;
}
}
return array[num];
}
It it also a bit faster because it uses the improved for loop.
At the end I wrote my function like this:
var getClosest = function(number, array) {
var current = array[0];
var difference = Math.abs(number - current);
var index = array.length;
while (index--) {
var newDifference = Math.abs(number - array[index]);
if (newDifference < difference) {
difference = newDifference;
current = array[index];
}
}
return current;
};
I tested it with console.time() and it is slightly faster than the other function.
The most efficient would be a binary search. However even simple solutions can exit when the next number is a further match from the current. Nearly all solutions here are not taking into account that the array is ordered and iterating though the whole thing :/
const closest = (orderedArray, value, valueGetter = item => item) =>
orderedArray.find((item, i) =>
i === orderedArray.length - 1 ||
Math.abs(value - valueGetter(item)) < Math.abs(value - valueGetter(orderedArray[i + 1])));
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log('21 -> 2', closest(data, 21) === 2);
console.log('22 -> 42', closest(data, 22) === 42); // equidistant between 2 and 42, select highest
console.log('23 -> 42', closest(data, 23) === 42);
console.log('80 -> 82', closest(data, 80) === 82);
This can be run on non-primitives too e.g. closest(data, 21, item => item.age)
Change find to findIndex to return the index in the array.
If the array is sorted like in your example, you can use a Binary Search for a better time complexity of O(log n).
const myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
const binaryClosestIdx = (arr, target) => {
let start = 0;
let end = arr.length - 1;
let mid = Math.floor((start + end) / 2);
while (1) {
if (arr[mid] === target) {
return mid;
}
else if (start >= end) {
break;
}
else if (arr[mid] > target) {
end = mid - 1;
} else {
start = mid + 1;
}
mid = Math.floor((start + end) / 2);
}
// Return the closest between the last value checked and it's surrounding neighbors
const first = Math.max(mid - 1, 0);
const neighbors = arr.slice(first, mid + 2);
const best = neighbors.reduce((b, el) => Math.abs(el - target) < Math.abs(b - target) ? el : b);
return first + neighbors.indexOf(best);
}
const closestValue = myArray[binaryClosestIdx(myArray, 80)];
console.log(closestValue);
How it works :
It compares the target value to the middle element of the array. If the middle element is bigger we can ignore every element after it as they are going to be even bigger. The same goes if the middle element is smaller, we can ignore every element before it.
If the target value is found we return it, otherwise we compare the last value tested with its surrounding neighbors as the closest value can only be between those 3 values.
Another variant here we have circular range connecting head to toe and accepts only min value to given input. This had helped me get char code values for one of the encryption algorithm.
function closestNumberInCircularRange(codes, charCode) {
return codes.reduce((p_code, c_code)=>{
if(((Math.abs(p_code-charCode) > Math.abs(c_code-charCode)) || p_code > charCode) && c_code < charCode){
return c_code;
}else if(p_code < charCode){
return p_code;
}else if(p_code > charCode && c_code > charCode){
return Math.max.apply(Math, [p_code, c_code]);
}
return p_code;
});
}
To Find Two Closest Number in array
function findTwoClosest(givenList, goal) {
var first;
var second;
var finalCollection = [givenList[0], givenList[1]];
givenList.forEach((item, firtIndex) => {
first = item;
for (let i = firtIndex + 1; i < givenList.length; i++) {
second = givenList[i];
if (first + second < goal) {
if (first + second > finalCollection[0] + finalCollection[1]) {
finalCollection = [first, second];
}
}
}
});
return finalCollection;
}
var counts = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
var goal = 80;
console.log(findTwoClosest(counts, goal));
You can use below logic to find closest number without using reduce function
let arr = [0, 80, 10, 60, 20, 50, 0, 100, 80, 70, 1];
const n = 2;
let closest = -1;
let closeDiff = -1;
for (let i = 0; i < arr.length; i++) {
if (Math.abs(arr[i] - n) < closeDiff || closest === -1) {
closeDiff = Math.abs(arr[i] - n);
closest = arr[i];
}
}
console.log(closest);
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
class CompareFunctor
{
public:
CompareFunctor(int n) { _n = n; }
bool operator()(int & val1, int & val2)
{
int diff1 = abs(val1 - _n);
int diff2 = abs(val2 - _n);
return (diff1 < diff2);
}
private:
int _n;
};
int Find_Closest_Value(int nums[], int size, int n)
{
CompareFunctor cf(n);
int cn = *min_element(nums, nums + size, cf);
return cn;
}
int main()
{
int nums[] = { 2, 42, 82, 122, 162, 202, 242, 282, 322, 362 };
int size = sizeof(nums) / sizeof(int);
int n = 80;
int cn = Find_Closest_Value(nums, size, n);
cout << "\nClosest value = " << cn << endl;
cin.get();
}
For a small range, the simplest thing is to have a map array, where, eg, the 80th entry would have the value 82 in it, to use your example. For a much larger, sparse range, probably the way to go is a binary search.
With a query language you could query for values some distance either side of your input number and then sort through the resulting reduced list. But SQL doesn't have a good concept of "next" or "previous", to give you a "clean" solution.

Categories

Resources