问题
Example: I have an array like this: [0,22,56,74,89]
and I want to find the closest number downward to a different number. Let's say that the number is 72
, and in this case, the closest number down in the array is 56
, so we return that. If the number is 100
, then it's bigger than the biggest number in the array, so we return the biggest number. If the number is 22
, then it's an exact match, just return that. The given number can never go under 0, and the array is always sorted.
I did see this question but it returns the closest number to whichever is closer either upward or downward. I must have the closest one downward returned, no matter what.
How do I start? What logic should I use?
Preferably without too much looping, since my code is run every second, and it's CPU intensive enough already.
回答1:
You can use a binary search for that value. Adapted from this answer:
function index(arr, compare) { // binary search, with custom compare function
var l = 0,
r = arr.length - 1;
while (l <= r) {
var m = l + ((r - l) >> 1);
var comp = compare(arr[m]);
if (comp < 0) // arr[m] comes before the element
l = m + 1;
else if (comp > 0) // arr[m] comes after the element
r = m - 1;
else // arr[m] equals the element
return m;
}
return l-1; // return the index of the next left item
// usually you would just return -1 in case nothing is found
}
var arr = [0,22,56,74,89];
var i=index(arr, function(x){return x-72;}); // compare against 72
console.log(arr[i]);
Btw: Here is a quick performance test (adapting the one from @Simon) which clearly shows the advantages of binary search.
回答2:
var theArray = [0,22,56,74,89];
var goal = 56;
var closest = null;
$.each(theArray, function(){
if (this <= goal && (closest == null || (goal - this) < (goal - closest))) {
closest = this;
}
});
alert(closest);
jsFiddle http://jsfiddle.net/UCUJY/1/
回答3:
Array.prototype.getClosestDown = function(find) {
function getMedian(low, high) {
return (low + ((high - low) >> 1));
}
var low = 0, high = this.length - 1, i;
while (low <= high) {
i = getMedian(low,high);
if (this[i] == find) {
return this[i];
}
if (this[i] > find) {
high = i - 1;
}
else {
low = i + 1;
}
}
return this[Math.max(0, low-1)];
}
alert([0,22,56,74,89].getClosestDown(75));
回答4:
Here's a solution without jQuery for more effiency. Works if the array is always sorted, which can easily be covered anyway:
var test = 72,
arr = [0,56,22,89,74].sort(); // just sort it generally if not sure about input, not really time consuming
function getClosestDown(test, arr) {
var num = result = 0;
for(var i = 0; i < arr.length; i++) {
num = arr[i];
if(num <= test) { result = num; }
}
return result;
}
Logic: Start from the smallest number and just set result
as long as the current number is smaller than or equal the testing unit.
Note: Just made a little performance test out of curiosity :). Trimmed my code down to the essential part without declaring a function.
回答5:
As we know the array is sorted, I'd push everything that asserts as less than our given value into a temporary array then return a pop of that.
var getClosest = function (num, array) {
var temp = [],
count = 0,
length = a.length;
for (count; count < length; count += 1) {
if (a[count] <= num) {
temp.push(a[count]);
} else {
break;
}
}
return temp.pop();
}
getClosest(23, [0,22,56,74,89]);
回答6:
Here is edited from @Simon. it compare closest number before and after it.
var test = 24,
arr = [76,56,22,89,74].sort(); // just sort it generally if not sure about input, not really time consuming
function getClosest(test, arr) {
var num = result = 0;
var flag = 0;
for(var i = 0; i < arr.length; i++) {
num = arr[i];
if(num < test) {
result = num;
flag = 1;
}else if (num == test) {
result = num;
break;
}else if (flag == 1) {
if ((num - test) < (Math.abs(arr[i-1] - test))){
result = num;
}
break;
}else{
break;
}
}
return result;
}
回答7:
Here's an ES6 version using reduce, which OP references. Inspired by this answer get closest number out of array
lookup array is always sorted so this works.
const nearestBelow = (input, lookup) => lookup.reduce((prev, curr) => input >= curr ? curr : prev);
const counts = [0,22,56,74,89];
const goal = 72;
nearestBelow(goal, counts); // result is 56.
Not as fast as binary search (by a long way) but better than both loop and jQuery grep https://jsperf.com/test-a-closest-number-function/7
来源:https://stackoverflow.com/questions/15203994/finding-the-closest-number-downward-to-a-different-number-from-an-array