0

I am fairly new to javascript and I'm having problems finding the most efficient way to calculate the problem below

I have an array of objects. Each object has a time stamp and a total field. I have a number saved as a variable and I want to loop through the array to find the timestamp of the object with the total field closest to my number.

This is a sorted array so the numbers are always increasing so for example the numbers could look like this:

Jan 125 
Feb 150 
Mar 200 
Apr 275 

If the number I have is 205 I would like to get the result Mar back.

They are objects taken from a mongoDb so look something like this

{TimeStamp: "2013-06-24 01:00", Delivered: 464, Queued: 39, Total: 503}
{TimeStamp: "2013-07-02 01:00", Delivered: 485, Queued: 37, Total: 522}
{TimeStamp: "2013-07-05 01:00", Delivered: 501, Queued: 41, Total: 542}
{TimeStamp: "2013-07-08 09:48", Delivered: 501, Queued: 64, Total: 565}
Keren Caelen
  • 1,466
  • 3
  • 17
  • 38
Marty Cochrane
  • 679
  • 2
  • 13
  • 26

4 Answers4

1

If the list is already sorted on the right field, you can use this code to find the minimum distance in O(n):

var data = [
    {total: 125, name: 'Jan'}, 
    {total: 150, name: 'Feb'}, 
    {total: 200, name: 'Mar'}, 
    {total: 275, name: 'Apr'}
];

function getClosest(arr, value) 
{
    var closest, mindiff = null;

    for (var i = 0; i < arr.length; ++i) {
        var diff = Math.abs(arr[i].total - value);

        if (mindiff === null || diff < mindiff) {
            // first value or trend decreasing
            closest = i;
            mindiff = diff;
        } else {
            // trend will increase from this point onwards
            return arr[closest];
        }
    }
    return null;
}

You keep track of the currently closest object and its corresponding (absolute) difference between the total and the searched value.

You keep updating those two values as long as the difference decreases. When that no longer happens you can return immediately, because you know it will never decrease afterwards.

To use it:

getClosest(data, 200);
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
0
var numbers = [122,231,323,53];
var myNumber = 200;
var difference = 9999; 
var nearest = null;
for (i = 0 ; i < numbers.lenght; i++){
    var candidate = numbers[i];
    var currentDifference = Math.abs(myNumber - candidate);
    if  (currentDifference  < difference) {
        nearest = candidate; difference = currentDifference;
    }
}
Edorka
  • 1,781
  • 12
  • 24
0

I've got this helpful generic function:

function min(ary, key) {
    return ary.map(function(x) {
        return [key ? key(x) : x, x]
    }).reduce(function(m, x) {
        return x[0] < m[0] ? x : m;
    })[1]
}

It finds a minimal element in the array using key as a comparison function. Applied to your problem:

 number = ...
 closestTimestamp = min(arrayOfRecords, function(record) {
       return Math.abs(number - record.total)
 }).TimeStamp;
georg
  • 211,518
  • 52
  • 313
  • 390
0

You can use a binary search for that value. Adapted from this answer:

function nearestIndex(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 // this[m] equals the element
            return m;
    }
    // now, l == r+1
    // usually you would just return -1 in case nothing is found
    if (l == arr.length) return r;
    if (r == 0) return 0;
    if (Math.abs(compare(arr[l])) > Math.abs(compare(arr[r]))) // "closer"
        return r;
    else
        return l;

}

var items = […];
var i=nearestIndex(items, function(x){return x.Total-532;}); // compare against 532
console.log(items[i].TimeStamp);
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375