2

This is kinda hard to phrase in a single line questions, but I'm looking for some advice/best practices for structuring data and writing a function in Javascript.

I have several items that change status regularly. My data contains an itemID, timestamp, and status. I am currently structuring it as an array of objects (for each item), with a history priority that contains the timestamps and status. (see below).

I'm looking for a function that will allow me to easily get the status of each object at a given time using the most recent past update. I'm not sure if my data structure will allow for this, or if it does, how to write the function. (for this example I'm going to shorten my timestamps to a 4 digit number)

 var items = [
      { id: 1,
        history: {1234: 'open', 1256: 'in-use', 1289: 'reset', 1293: 'open'},
      { id: 2,
        history: {1230: 'open', 1290: 'in-use'},
      { id: 3,
        history: {1238: 'open', 1241: 'in-use', 1251: 'reset'}
 ]

I'd like to be able to have a function like this:

 getStatus(1260);

and get back

 {1: 'in-use', 2: 'open', 3: 'reset'}

Each id, with the status it was in at the time passed in based on the most recent history record prior to the queried time.

I am not at all attached to this data structure. I also tried having the history an array of objects containing the time and the status, but that means I have to loop through the entire array every time. My biggest problem is that my head is pushing me to an SQL method for doing this, but I'm stuck in client-side Javascript...

My questions: What is the best data structure for this? and How would I go about writing my getStatus() function?

Thanks!

whiteatom
  • 1,456
  • 2
  • 14
  • 33

2 Answers2

2

I also tried having the history an array of objects containing the time and the status, but that means I have to loop through the entire array every time.

Not if you had the array sorted, as you then can access the most recent date directly. Also you can use binary search for getting the state at a specific timestamp. With the object you currently have, you always have to enumerate all properties to find the best-matching.

var items = [
  { id: 1,
    history: [
      { timestamp: 1234, status: 'open'},
      { timestamp: 1256, status: 'in-use'},
      { timestamp: 1289, status: 'reset'},
      { timestamp: 1293, status: 'open'}
    ]
  },
  …
];
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 // this[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
}
// example:
function insertItem(history, timestamp, status) {
    var i = index(history, function(item) {
        return item.timestamp - timestamp;
    });
    history.splice(i+1, 0, {timestamp: timestamp, status: status});
}

function getStatus(time) {
    var result = {};
    function comparefn (item) {
        return item.timestamp - time;
    }
    for (var i=0; i<items.length; i++) {
        var pos = index(items[i].history, comparefn);
        result[items[i].id] = pos == -1
          ? undefined
          : items[i].history[pos].status;
    }
    return result;
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Wow.. ok.. so I will switch to the array for sure. Now I need to spend some time understanding this code..... – whiteatom Jan 17 '13 at 01:21
  • Ok.. I have some confusion about the index function - it looks like a binary sort, but I'm not sure what the bitwise operation is doing... specifically var m = l + ((r - l) >> 1); Is that going to divide by 2 and round? – whiteatom Jan 17 '13 at 01:33
  • Yes. It is short for `Math.floor( (r-l) / 2)` by using the [bitwise right-shift operator](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators#>>_(Sign-propagating_right_shift)). And it's not a binary sort, but a [binary search](http://en.wikipedia.org/wiki/Binary_search_algorithm) :-) – Bergi Jan 17 '13 at 01:41
  • Ok. I think i got it. Thanks a lot! – whiteatom Jan 17 '13 at 02:44
  • Bergi, I know I'm 2 weeks late, but I'm hoping you could point me in the right direction.. same situation as the original post, but now we need to add a search for the most recent "in-use" before a given time. Right now I'm passing a filtered array to the code you gave me, but this is essentially 2 searches instead of one. Do you have any suggestions on how to do this more efficiently? – whiteatom Jan 31 '13 at 00:20
  • I think filtering once and searching (with above code) multiple times is quite OK. If you need to search only once, you could binary search for a time in the original array, and then iterate backwards until you find a "in-use". – Bergi Jan 31 '13 at 00:38
0

You could use a loop:

// for each entry:
var n2 = n;
while (typeof items[n2] == "undefined" && n2 >= -1) {
    n2--;
}
if (n != -1) {returnedArray[idBeingProcessed] = items[n2];}
else {alert("Error handling here");}
// repeat for each item (id 1, id 2...)

This will stop if an answer has been found. It may be inefficient, but hey, it works :-)

Also, consider using an array for your history objects if applicable.

ameed
  • 1,132
  • 6
  • 25
  • Do you really recommend looping all available timestamps? – Bergi Jan 17 '13 at 01:28
  • As I said, it's inefficient but the only solution I could think of. Besides, you only loop through timestamps where `entry < n < query`, not the whole object/array/whatever. Again, not the best, but it works and it's not as bad as looping an array from A-Z.. – ameed Jan 17 '13 at 02:53
  • Depends on how sparse the array/object is. Think of timestamps in milliseconds :-) Still, even for the sample data the OP provided I'd expect a for-in loop to be much faster. And your solution does not catch if there is nothing found, it would loop until `-Infinity`… – Bergi Jan 17 '13 at 03:00
  • @Bergi Fixed the `-Infinity` bug; nice catch. I did check out your thing; interesting that looping the whole array will be better than my solution. Will take into consideration; thanks! – ameed Jan 17 '13 at 03:19