Saurabh Palatkar Saurabh Palatkar - 1 month ago 8
Javascript Question

Optimized way to search an object array based on multiple key-value pairs

I wrote a generic extension method which will return an item which matches all the given search criterion (lookUpArray). Here is the Demo.

/***************************Extension Method****************************/

var utils = {};
// Could create a utility function to do this
utils.searchInArray = function (lookUpArray, caseSensitiveSearch) {
if (!lookUpArray || lookUpArray.length <= 0)
return null;

caseSensitiveSearch = caseSensitiveSearch || false;
var self = this;
var item = null;
for (var index = 0; index < self.length; index++) {
item = self[index];
var exist = true;
for (var i = 0; i < lookUpArray.length; i++) {
if (item[lookUpArray[i].key] === lookUpArray[i].value) {
exist = exist * true;
} else { exist = exist * false; }
}
if (exist)
return item;
};
return exist ? item : null;
};

// or we could create a function on the Array prototype indirectly
Array.prototype.excSearchObjArrayForKeyValue = utils.searchInArray;



/***************************Extension Method****************************/


var inputObjectArray= [
{ emailType: 'primary', id: 1, username: 'saurabh', email: 'test@gmail.com', phone: '123' },
{ emailType: 'additional', id: 2, email: 'test2@gmail.com' },
{ emailType: 'additional', id: 2, email: 'test2@gmail.com', username:'spp' }
];

//Below is the search criterion array. Extension method should return only that object which
//matches complete below lookUpArray
var lookUpArray = [{ key: 'emailType', value: 'additional' }, { key: 'id', value: 2 }];

var result = inputObjectArray.excSearchObjArrayForKeyValue(lookUpArray);
console.log(result);


Is there any possibility to optimize (performance) above searching?

Answer

It depends on your use case. If you will be running the search function fairly often (compared to how often the array is modified), and if you have a limited number of possible keys to search across, you might find it worthwhile to create and maintain an index-like structure. Right now your lookup is an O(m*n) operation, where m is the number of keys and n is the number of items in the array. With a proper data structure in place, the lookup could become an O(m) operation instead. Since I'm guessing n is likely to be the bigger number by far, this could make the search scale far more efficiently.

If it doesn't make sense to do that, then you should at least short-circuit your inner loop.

            var self = this;
            for (var index = 0; index < self.length; index++) {
                var item = self[index];
                var matches = true;
                for (var i = 0; i < lookUpArray.length; i++) {
                    var lookUpItem = lookUpArray[i];
                    if (item[lookUpItem.key] !== lookUpItem.value) {
                        matches = false;
                        break;
                    }
                }
                if(matches) {
                    return item;
                }
            };
            return null;

Or, as nnnnnn suggests, you could accomplish the same thing more concisely with a label:

            var self = this;
            outer:
            for (var index = 0; index < self.length; index++) {
                var item = self[index];
                for (var i = 0; i < lookUpArray.length; i++) {
                    var lookUpItem = lookUpArray[i];
                    if (item[lookUpItem.key] !== lookUpItem.value) {
                        continue outer;
                    }
                }
                return item;
            };
            return null;

If you're using ES6, you could even leverage the .find() and .every() functions.

            var self = this;
            return self.find(item => 
                lookUpArray.every(lookUpItem => 
                    item[lookUpItem.key] === lookUpItem.val));

And I would advise against adding this method to the Array prototype. I'd just make it a utility method.