Finding the closest level by experience [duplicate] - javascript
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.
Related
Function that inserts a new value in the middle of an array? [duplicate]
This question already has answers here: array.splice(index, 1) returns array with removed item (3 answers) Closed 4 months ago. I have to use a.splice(index, 0, value) to solve. this is what I have so far, but the value for final is an empty array(i initialized to null for debugging purposes); I think the problem is with my splice, because my output is just an empty array but i'm pretty sure my syntax is correct. Input a == [7, 10, 15, 54] x = 99 Output: [7, 10, 99, 15, 54] function solution(a, x) { let count = a.length+1 let mid = count/2 let final = null if (mid % 2 === 0) { final = a.splice(a[mid], 0, x) } else { let middle = mid - 1 final = a.splice(a[middle], 0, x) } return final } Edit: I see the comment and have amended the code to this: let count = a.length+1 let mid = count/2 if (mid % 2 === 0) { a.splice(mid, 0, x) } else if (mid % 2 == 1) { let middle = mid-1 a.splice(middle, 0, x) } return a } but this input fails because it doesn't want to insert the new value? **Input: a: [64, 38, 22, 27, 62, 41] x: 90 Output: [64, 38, 22, 27, 62, 41] Expected Output: [64, 38, 22, 90, 27, 62, 41]**
Reading offical document of splice(),we can find below description: Return value An array containing the deleted elements. If only one element is removed, an array of one element is returned. If no elements are removed, an empty array is returned. Since you are not delete element,that's the reason,so you need to return the element directly function solution(a, x) { let count = a.length+1 let mid = count/2 if (mid % 2 === 0) { a.splice(mid, 0, x) } else { let middle = mid - 1 a.splice(middle, 0, x) } return a } let data = [7, 10, 15, 54,55] let x = 99 console.log(solution(data,x))
First splice modify the original array. So you will have to return the original array and there is no need of final variable here. Secondly you have to use Math.floor or Math.ceil as mid - 1 will still be a floating point number and lastly a[middle] in a.splice(a[middle]...) need to be replaced only with middle function solution(a, x) { let count = a.length + 1 let mid = count / 2 let final = null if (mid % 2 === 0) { a.splice(a[mid], 0, x) } else { let middle = Math.ceil(mid - 1); a.splice(middle, 0, x) } return a; } console.log(solution([7, 10, 15, 54], 99))
Check an number in an array is a powers number
How to check which number is a power in this array? arr = [16, 32, 72, 96] output: [16, 32] because 16 = 4^2 and 32 = 2^5 It's not a solution about power of 2, it's about power of n. This is my actual code : let array1 = [16, 32, 72]; function findPowofNum(array1) { // var result = []; if (array1.length == 1) return true; for(let i = 2; i * i <= array1.length; i++) { let value = Math.log(array1/length) / Math.log(i); if((value - Math.floor(value)) < 0.00000001) return true; } return false; } console.log(findPowofNum(array1)); Can you give me a example for this solution by javascript?
How about arr = [16, 32, 72, 96].filter( value => Number.isInteger(Math.log2(value)) )
You can use a custom boolean function to iterate through the elements and append to an empty array as you check like this.. function isPowerofTwo(n){ return (Math.ceil((Math.log(n) / Math.log(2)))) == (Math.floor(((Math.log(n) / Math.log(2))))); } let arr= [16, 32, 72, 96]; let values=[] for(let i=0;i<arr.length;i++){ if(isPowerofTwo(arr[i])){ values.push(arr[i]); } } console.log(values);
const numbers = [16, 32, 72, 96]; const power = numbers.filter(isPowerOfTwo); function isPowerOfTwo(n) { if (n == 0) return 0; while (n != 1) { if (n%2 != 0) return 0; n = n/2; } return 1; } console.log(power); To know more about this visit visit: https://www.geeksforgeeks.org/program-to-find-whether-a-given-number-is-power-of-2/
I think it is 2^4 not 4^2 and what I believe is you want to check if the number is and root of 2 or not Solution let arr=[16,32,72,96]; let i=1; for(i=1;i<=10;i++){ // this loop will run up to 2^10 let k=Math.pow(2,i);if(k==arr[i-1]){ //do whatever you want to do with the number } }
This will return true if at least one element y of the array is equal to basepower, where base and power are integers. The basic idea is to simply try each possible base, compute the value of power needed to make basepower equal to the value, and check to see if power is very close to an integer. This code only works for positive values, and probably won't work for for value in the upper range near Number.MAX_SAFE_INTEGER, though I really haven't tested it. let array1 = [7, 13, 72, 27]; function isPowOfNum(val) { // smallest base is 2, largest is sqrt(val) for (let base=2; base <= Math.sqrt(val); base++) { // if val = base ** x then log(val) = x * log(base), x = log(val) / log(base) // and x is an integer let power = Math.log(val) / Math.log(base); if (Math.abs(power - Math.round(power)) < 0.000001) { return true; } } return false; } function findPowofNum(array1) { if (array1.length === 1) return true; return array1.some(isPowOfNum); } console.log(findPowofNum(array1));
Simplify array from many values to fewer
I have an array with many numbers (165) and want to 'simplify' the array to fewer numbers (50). The reduced array should still be representative for the 165 original numbers. For example: If I had this array with 8 values [2, 4, 3, 8, 1, 4, 9, 3] and would ask to reduce it to 4 values, I want to receive this array [3, 5.5, 2.5, 6] Currently I have a function that works when the reduced number and the original number can be divided (as with 8 and 4) but when I try using it with let's say 165 values that should be simplified to 50 values it returns me 54 values: const array = [28, 71, 64, 116, 128, 8, 78, 172, 142, 96, 12 ... 165 values] const valuesToSum = Math.floor(array.length / 50); // 50 is the wanted array length for (let i = 0; i < array.length; i++) { if (i % valuesToSum !== 0 || i === 0) { sum += array[i]; } else { returnArray.push(sum / valuesToSum); sum = 0; } } return returnArray; In the end this should be a JavaScript function but if someone could explain me this on a mathematical level it would help me a lot.
In order to get the exact number of groups you want, you can't round off the number of elements to group. For instance if you want to reduce from 165 to 50, some of the groups will have 3 elements, some will have 4. To do this, use nested loops. The outer loop increments by the size of the group, while the inner loop increments by 1 within the group. The rounding happens when you convert this inner index to the array index. const array = [28, 71, 64, 116, 128, 8, 78, 172, 142, 96, 12] function reduceArray(array, newSize) { const returnArray = []; const valuesToSum = array.length / newSize; for (let i = 0; i < array.length; i += valuesToSum) { let sum = 0; let j; let start_i = Math.floor(i); for (j = start_i; j < Math.min(start_i + valuesToSum, array.length); j++) { sum += array[j]; } returnArray.push(sum/(j - start_i)); } return returnArray; } console.log(reduceArray(array, 4)); const bigArray = []; for (let i = 0; i < 165; i++) { bigArray.push(Math.floor(Math.random() * 200)); } let result = reduceArray(bigArray, 50); console.log(result.length); console.log(result);
javascript - find subset that gives maximum sum under a certain limit (subset sum )
I have an array with some integer values, and I need to get a subset of them that gives me the maximum sum that is inferior to a given value. So let's say I have this array: [40, 138, 29, 450] I would like to get a subset of this array that maximize the sum but is inferior to a limit given by the user, let's say 250. In this case it should return [139, 40, 29]. I had a look at this question and related answer, and tried to use the code given, but I didn't understand it very well. Anyway I've tried it, setting the min to 0 and the max to the limit given, but it keeps returning me "5" that is not correct, since the limit is like 300 and the numbers in my array are all over 50. I couldn't find anything that could help me, so I'm asking if anyone could give me some code or pseudocode to understand how to do this.
Basically you could either add the element at the index to a temporary array or not. Then check if the index reaches the lenght of the array or if the sum is greater than the wanted sum, then either check the sum and add the temp array to the result set, or not. Proceed until all indices are visited. function getCombinations(array, sum) { function add(a, b) { return a + b; } function fork(i, t) { var r = (result[0] || []).reduce(add, 0), s = t.reduce(add, 0); if (i === array.length || s > sum) { if (s <= sum && t.length && r <= s) { if (r < s) { result = []; } result.push(t); } return; } fork(i + 1, t.concat([array[i]])); fork(i + 1, t); } var result = []; fork(0, []); return result; } console.log(getCombinations([40, 138, 29, 450], 250)); .as-console-wrapper { max-height: 100% !important; top: 0; }
A fast and compact solution: function maxSum(input, limit) { const sums = {}; let max = 0; const collectSums = (n, i, values) => { for (; i < input.length; i++) { const sum = n + input[i]; if (sum <= limit) { values.push(input[i]); if (sum >= max && values.length > 1) { max = sum; sums[max] = values.slice(); // https://jsperf.com/copying-an-array } collectSums(sum, i + 1, values); } } values.pop(); }; collectSums(0, 0, []); return sums[max] || []; } Apart from the necessary iterations of the input this solution tries to keep complexity low by not using costly array operations. Only a found subset has to be copied to keep track of possible combinations. Still, there are probably more clever solutions possible to improve performance. The method will return the last found combination, this means that two input lists with the same values in different order might yield different results: maxSum([1, 4, 200, 5], 205) == [200, 5]; maxSum([5, 200, 1, 4], 205) == [200, 1, 4]; If you want all possible combinations replace this line: sums[max] = values.slice(); // https://jsperf.com/copying-an-array with this: sums[max] = sums[max] || []; sums[max].push(values.slice()); All combinations are then returned: maxSum([1, 4, 200, 5], 205) == [[1, 4, 200], [200, 5]]; But note that this will always return an array of arrays, even when there is only one possibility: maxSum([40, 138, 29, 450], 250) == [[40, 138, 29]];
Here's a brute force solution. First we get every possible combination of values from the original array, take their sum, and see which of those gets us the highest value without overflowing the given maximum. var ary = [40, 138, 29, 450]; // Function to construct a power set. A power set is just the set of // all possible subsets of some given set. function makePowerSet(ary) { powerSet = []; for (let ps = 1; ps <= Math.pow(2, ary.length); ps++) { subset = []; for (let i = 0; i < ary.length; i++) { if (ps & Math.pow(2, i)) subset.push(ary[i]); } powerSet.push(subset); } return powerSet; } // Function to calculate the sum of an array. function getSum(ary) { return ary.reduce((sum, cur) => { return sum + cur; }, 0); } function getSubsetBelow(val, ary) { let bestSoFar; let bestSoFarSum = 0; for (let subset of makePowerSet(ary)) { const sum = getSum(subset); if (sum > val) continue; if (sum > bestSoFarSum) { bestSoFar = subset; bestSoFarSum = sum; } } console.log("Got a best sum value of", bestSoFarSum, "with the subset", bestSoFar); } getSubsetBelow(250, ary) This seems very similar to the knapsack problem, which is NP-hard, so I don't know if you'll ever be able to find an efficient algorithm for this. However, there are definitely a few optimizations that can be made to what I've written here, for example, any element of the array already greater than the limit can't be part of the solution (easy way to eliminate 450).
#Find a maximum sum of a compact subsequence of array elements. import sys def solution(A): max_ending = max_slice = -sys.maxsize if(len(A)==1): return A[0] else: for a in A: max_ending =max(a,max_ending + a) max_slice = max(max_slice, max_ending) return max_slice
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.