1

A number of times (especially in MVC scenarios) I had to use an array to store objects, but I also had to access the objects by a property of the stored objects. I may then iterate the array, but I guess (guess because I really know little about Javascript performance) this will be slow already with hundreds of objects and hundreds of iterations. I then build an index like so:

var arr = [];
var index = {};

var o1 = {id: 0, key: "A", data: "X"};
var o2 = {id: 1, key: "B", data: "Y"};

arr.push(o1);
arr.push(o2);
index[o1.key] = o1;
index[o2.key] = o2;

var lookupKey = "B";

// modify object
arr[1].data = "Z";

// get data for key "B" - alerts "Z"
alert(index[lookupKey].data);

(http://jsfiddle.net/timvdh/WT53x/3/)

Of course I have to build and maintain this index... I was wondering if there is a better method?

I guess there is a lib that includes "SortedDictionaries" (the name of such generic collection in .NET) - and I would like to know about is. However, I am also working with specialized arrays (e.g. knockout's observableArray) and I would need a way to index that array, too. Or I need to know that it is simply not worth doing what I am doing with the index.

TvdH
  • 1,088
  • 14
  • 29
  • See http://stackoverflow.com/questions/1076658/javascript-array-associative-and-indexed and http://stackoverflow.com/questions/9273157/javascript-how-to-get-index-of-an-object-in-an-associative-array – Etheryte Mar 13 '14 at 12:35
  • 1
    An alternate idea I had was to map the keys to their indexes at the time of the push, for an easy re-trace to get the array index that hold that key's relative object. [Quick mockup fiddle](http://jsfiddle.net/SmokeyPHP/ZVA5U/3/) – MDEV Mar 13 '14 at 14:48
  • @SmokeyPHP: Your mockup demonstrates something like a "SortedDictionary" object, very good. I am wondering - there is no standard for that? The disadvantage of your custom class is, that the indexing cannot be used for other, specialized arrays if one does not want to mess with the source code of the array. – TvdH Mar 14 '14 at 07:40
  • @TvdH Indeed, it isn't very flexible. If only used for the situation described above it should work very well, otherwise, if you don't mind potentially breaking array loops (if used), you could use the class to add tracking properties to the array itself. `arr.keyMap = {} ... ` etc – MDEV Mar 14 '14 at 11:29

2 Answers2

1

Sounds like you're heading in the right direction, but there's a couple of tricks you can employ to get the answer you need without building a separate index.

var arr = [];

arr.push({id: 0, key: "A", data: "X"});
arr.push({id: 1, key: "B", data: "Y"});

arr[1].data = 'Z';

var lookup = 'B';

Now here you can use the filter method* on the array to pull out the object with the correct lookup value.

var result = arr.filter(function(el) {
  return el.key === lookup;
});

Note that results will be an array of all the matches that are found. So, if you only have one element in the array:

console.log(result[0].data) // Z

Let's add another object to the array and rerun the filter:

arr.push({id: 2, key: "B", data: "E"});

And use a loop on the results:

for (var i = 0, l = result.length; i < l; i++) {
  console.log(result[i].data); // Z and E
}

Fiddle

Ultimately you might want to encapsulate this into a function like so:

function findThings(prop, value) {
  return arr.filter(function(el) {
    return el[prop] === value;
  });
}

console.log(findThings('key', 'B')) // an array of two objects that you can loop over

&ast; Note that for older browers you'll need to use a filter polyfill.

Andy
  • 61,948
  • 13
  • 68
  • 95
  • Isn't filter "just" a more luxurious iteration? – TvdH Mar 13 '14 at 12:44
  • 1
    Sure, but it doesn't need you to build a completely separate index to find the data you need. – Andy Mar 13 '14 at 12:47
  • @TvdH, also, what if you had two objects in the array with the same `key`? I assume that `id` is unique, but `key` wouldn't be. `filter` allows you to grab _all_ the objects that satisfy a situation, not just one. – Andy Mar 13 '14 at 13:19
  • True, "key" must be unique (and it is in my applications). I understand that filter() is much better to handle. I am not yet convinced that indexing is not worth the effort for the use case hundreds, or let's say exactly 100 entries and 100 lookups. I will give it try and report. – TvdH Mar 13 '14 at 13:37
  • http://jsfiddle.net/timvdh/WT53x/5/ - it took 0 ms with index vs. 2-5 ms for filter method. Not relevant for web applications. For 1000 elements and seeks it becomes relevant (0 vs. 80ms). It is probably more of an node.js programmer's issue. – TvdH Mar 13 '14 at 14:04
  • I take this as the answer because the filter() method seems to make most sense in common scenarios and nobody pointed out a common alternative, although the specialized class that SmokeyPHP proposed is pretty good. – TvdH Mar 14 '14 at 11:37
0

The best solution I see is, sure, iterate the array.

ECMAScript 6 may introduce the find() method. Until then, ou may create a simple function as bellow (not tested):

function obj_find(arr, key, value) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i][key] == value) return arr[i];
    }

    return null;
}

I don't think it'll slow down too much your app, since the function returns as soon as it finds the value. But you have thousands of elements to search you may add a cache to speed up the function.

paulodiovani
  • 1,208
  • 2
  • 16
  • 34