davidnc
davidnc

Reputation: 13

More efficient array search

I'd like to pull a particular object from an array of objects based on a unique property of that object (ie, a key).

In the following, I'm searching for an element in 'arr' where the key is 8.

var myElement = arr.filter(function(element) {
  return element.key === 8;
});

This works, but every time this runs, it will iterate through all elements in the array, even after the correct element has been found. For example, if it finds myElement at index 4, but there are 100 elements in the array, the snippet is running 25x more than it needs to.

Is there a more efficient way, or a way to terminate filter() when it has found myElement?

I feel like I've missed something obvious...

Upvotes: 1

Views: 95

Answers (5)

arao6
arao6

Reputation: 3376

Why reinvent the wheel? There are a lot of similar answers here, so I'll share a different approach--by far my favorite approach. There is an excellent library called linq.js (both standalone and jQuery plugin versions) which makes searching, filtering, sorting, etc. a breeze.

var myElement = Enumerable.From(arr).FirstOrDefault(null,         function(element) {
      return element.key === 8;
});

In the above example, the first element that matches the conditions is returned. If nothing is found, null is returned (the first parameter is the default value to return).

Upvotes: 0

rexcfnghk
rexcfnghk

Reputation: 15502

You actually want a find function that returns when the first occurrence is found. You can use Array.prototype.find if you are on ECMAScript 6 spec or you can implement a simple find like:

function find8(arr) {
    // type checks skipped for brevity
    var i = 0,
        j = arr.length;

    for (; i < j; i++) {
        if (arr[i].key === 8) {
            return arr[i];
        }
    }

    throw new Error('Not found');
}

If you want a more generic solution that accepts predicates (i.e. functions that return a boolean value), you can write a more generic function like:

function find(arr, predicate) {
    // Again, type checks skipped for brevity
    var i = 0,
        j = arr.length;

    for (; i < j; i++) {
        // Note the condition is now a call to the predicate function
        if (predicate(arr[i])) {
            return arr[i];
        }
    }

    throw new Error('Not found');
}

Upvotes: 2

Reut Sharabani
Reut Sharabani

Reputation: 31349

I'd handle this with a simple for loop (used a function for generic re-use):

function findObject(arr, cond)
    for (i = 0; i < arr.length; i++) {
        if (cond(arr[i]){
            return arr[i];
        }
    }
}

// now call fincObject(myArray, function(element){element.key === 8})

Or, if you know you are going to do this many times, create a mapping which should be a lot faster:

function makeMapping(arr, keyFunc){
    var mapping = {};
    for (var i = 0; i < arr.length; i++) {
        mapping[keyFunc(arr[i])] = arr[i];
    }
    return mapping;
}

This returns an object mapping 8 to the object with key.id === 8. Your keyFunc would be:

function keyFunc(element){
    return element.key;
}

Upvotes: 1

aroth
aroth

Reputation: 54854

It sounds like you'd be better off with something like this:

function findByKey(items, key) {
    for (var index = 0; index < items.length; index++) {
        var item = items[index];
        if (item.key === key) {
            return item;
        }
    }
    return null;
}

Note that if your list is sorted, you can improve upon that by doing a binary-search instead. Though personally I'd suggest hashing (assuming that the same key isn't used twice). Something like:

var array = [/* your data here... */];
var map = {};

//do this once; or once each time your array changes
for (var index = 0; index < array.length; index++) {
    var item = array[index];
    map[item.key] = item;
}

//now find things by doing this
var itemWithKey8 = map[8];

Upvotes: 1

Joel Cox
Joel Cox

Reputation: 3469

for (var i=0; i<arr.length; i++) {
    if (arr[i].key === 8) {
        console.log('FOUND ITEM AT KEY '+8);
        break;
    }
}

Upvotes: 1

Related Questions