6

Example

Link: http://jsfiddle.net/ewBGt/

var test = [{
    "name": "John Doo"
}, {
    "name": "Foo Bar"
}]

var find = 'John Doo'

console.log(test.indexOf(find)) // output: -1
console.log(test[find]) // output: undefined

$.each(test, function(index, object) {
    if(test[index].name === find)
        console.log(test[index]) // problem: this way is slow
})

Problem

In the above example I have an array with objects. I need to find the object that has name = 'John Doo'

My .each loop is working, but this part will be executed 100 times and test will contain lot more objects. So I think this way will be slow.

The indexOf() won't work because I cannot search for the name in object.

Question

How can I search for the object with name = 'John Doo' in my current array?

Ron van der Heijden
  • 14,803
  • 7
  • 58
  • 82
  • 8
    Use a `for` loop *(not `for-in`)* to improve performance, and `break` as soon as the match is found. –  Apr 02 '13 at 14:13
  • 3
    Unless your array is sorted by that `name`, or you have another data structure (like a map keyed by name), there is no way to find it, other than an exhaustive search. – Rob I Apr 02 '13 at 14:14
  • Duplicate of http://stackoverflow.com/questions/5579678/jquery-how-to-find-an-object-by-attribute-in-an-array – NilsH Apr 02 '13 at 14:15
  • If you're saying the loop itself will be performed 100 times, then I'd guess that there are other ways to optimize, but more info would be needed. –  Apr 02 '13 at 14:16
  • Do your objects hold just the "name" property? – Luca Fagioli Apr 02 '13 at 14:27
  • @LucaFagioli No, more information, but I only seek for name. – Ron van der Heijden Apr 02 '13 at 14:31
  • You have to loop then. Check my answer. – Luca Fagioli Apr 02 '13 at 15:23
  • 1
    @Bondye - do you have control over the structure of the data? If the names are unique and you could make them the key to the other data, that opens up a possibility of simplifying it significantly. – talemyn Apr 02 '13 at 15:41

5 Answers5

7

I have done sometimes "searchable map-object" in this kind of situation. If the array itself is static, you can transform in to a map, where array values can be keys and map values indexes. I assume values to be unique as in your example.

Lo-Dash (www.lodash.com) has create selection of utils for easily looping etc. Check it out!

Note: But often you really don't have to worry about looping trough array with 100 elements.

  • 2
    E.g. _.indexOf() of Lo-Dash can be told if your array is already sorted. Then it will use binary search trough your array (really fast). – Matti Simperi Apr 02 '13 at 14:29
  • 1
    Not sure why this answer was down voted. It's a very good solution. – Chuck Conway Apr 29 '14 at 02:36
  • This is a fast solution if you need to search over the same array multiple times, but be aware that it can cost much in memory terms, if you deal with large arrays, because you have to build another in-memory object. The time cost of transforming the array to map is always O(n), not just in the worst case as in a old fashioned for loop. Once transformed, this solution leads to O(1) executions time. If you need to search just once, or if you are worried about memory, best to use the for loop approach. – Luca Fagioli Jun 15 '15 at 17:02
7

jQuery $.grep (or other filtering function) is not the optimal solution.

The $.grep function will loop through all the elements of the array, even if the searched object has been already found during the loop.

From jQuery grep documentation :

The $.grep() method removes items from an array as necessary so that all remaining items pass a provided test. The test is a function that is passed an array item and the index of the item within the array. Only if the test returns true will the item be in the result array.

Provided that your array is not sorted, nothing can beat this:

var getObjectByName = function(name, array) {

    // (!) Cache the array length in a variable
    for (var i = 0, len = test.length; i < len; i++) {

        if (test[i].name === name)
            return test[i]; // Return as soon as the object is found

    }

    return null; // The searched object was not found

}
Luca Fagioli
  • 12,722
  • 5
  • 59
  • 57
  • 3
    Ehy Bondye, you're welcome. I got very curious about what you said about the pre-increment being faster than the post-increment, but it turned out that this was true only for the gcc compiler, that was "used to generate sub-optimal code for post-increment and that is why pre-increment was used then. Compilers have come a long way and programmers don't need to think in weird way to outsmart the compilers anymore". Nowadays, it is a kind of legend. Interesting though :) – Luca Fagioli Apr 02 '13 at 21:03
3

If you just want to find out if the value is there, you can use lodash's includes function like this:

var find = 'John Doo'

[{ "name": "John Doo" }, { "name": "Foo Bar" }].some(function (hash) {
    if (_.includes(hash, find)) return true;
});

Documentation:

tomekwi
  • 2,048
  • 2
  • 21
  • 27
0

Perhaps you should use the $.grep functionality in jQuery:

var test = [{
    "name": "John Doo"
}, {
    "name": "Foo Bar"
}]

var find = 'John Doo'

var found = $.grep(test, function(obj){
    return obj['name'] == find;
});

console.log(found);

Fiddle: http://jsfiddle.net/ewBGt/3/

sbeliv01
  • 11,550
  • 2
  • 23
  • 24
  • 1
    It cannot improve the performance. This function "filters" an array, returning another array, not the object itself. This means for sure that it will process the entire array, it won't break the loop execution when it finds the searched element. – Luca Fagioli Apr 02 '13 at 14:41
0

The only thing you can possibly do is use build-in array methods (if available) in preference over doing the looping yourself – the filter method would be applicable here.

But I expect that JS libraries like jQuery used by sbeliv01 in his answer already check for that internally (and provide a fallback solution if these array methods are not available natively) – so don’t expect a massive performance boost.

CBroe
  • 91,630
  • 14
  • 92
  • 150