简体   繁体   中英

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?

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.

You can utilize array functions like Array.prototype.filter and Array.prototype.every instead of iterating through items yourself:

var utils = {
  searchInArray: function(targetArray, lookupArray, caseSensitiveSearch) {
    return targetArray.filter(function(x) {
      return lookupArray.every(function(lookup) {
        if (x[lookup.key] === undefined)
          throw new Error('No ' + lookup.key + ' property in object ' + x);

        if (typeof x[lookup.key] !== typeof lookup.value)
          throw new Error('Type mismatch on property ' + lookup.key ' + in object ' + x);

        if (typeof lookup.value === 'string' && caseSensitiveSearch)
          return x[lookup.key].toLowerCase() === lookup.value.toLowerCase();
        else
          return x[lookup.key] === lookup.value;
      });
    });
  }
};

Working demo snippet:

 var utils = { searchInArray: function(targetArray, lookupArray, caseSensitiveSearch) { return targetArray.filter(function(x) { return lookupArray.every(function(lookup) { if (x[lookup.key] === undefined) throw new Error('No ' + lookup.key + ' property in object ' + x); if (typeof x[lookup.key] !== typeof lookup.value) throw new Error('Type mismatch on property ' + lookup.key ' + in object ' + x); if (typeof lookup.value === 'string' && caseSensitiveSearch) return x[lookup.key].toLowerCase() === lookup.value.toLowerCase(); else return x[lookup.key] === lookup.value; }); }); } }; 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' } ]; var lookUpArray = [{ key: 'emailType', value: 'aDdItIoNaL' }, { key: 'id', value: 2 }]; var result = utils.searchInArray(inputObjectArray, lookUpArray, true); console.log(result); 

You can use the Arrays.find function.

The find() method returns a value in the array, if an element in the array satisfies the provided testing function. Otherwise undefined is returned.

Have a look at this examples:

var inventory = [
    {name: 'apples', quantity: 2},
    {name: 'bananas', quantity: 0},
    {name: 'cherries', quantity: 5}
];

function findCherries(fruit) { 
    return fruit.name === 'cherries';
}

console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }

You can also write your custom logic, for eg. to find prime numbers:

function isPrime(element, index, array) {
  var start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
console.log([4, 5, 8, 12].find(isPrime)); // 5

Pls mark correct if helpful.

To get first matching obj you can try this

utils.searchInArray = function(lookUpArray, caseSensitiveSearch) {
    if (!lookUpArray || lookUpArray.length <= 0)
      return null;
    caseSensitiveSearch = caseSensitiveSearch || true;
    var self = this;
    return self.find(function(obj) {
      return lookUpArray.every(function(lookup) {
        if (typeof lookup.value === 'string' && caseSensitiveSearch)
          return obj[lookup.key].toLowerCase() === lookup.value.toLowerCase();
        else
          return obj[lookup.key] === lookup.value;
      });

    });
  };

To get the all matching object

utils.searchInArray = function(lookUpArray, caseSensitiveSearch) {
    if (!lookUpArray || lookUpArray.length <= 0)
      return null;
    caseSensitiveSearch = caseSensitiveSearch || true;
    var self = this;
    return self.filter(function(obj) {
      return lookUpArray.every(function(lookup) {
        if (typeof lookup.value === 'string' && caseSensitiveSearch)
          return obj[lookup.key].toLowerCase() === lookup.value.toLowerCase();
        else
          return obj[lookup.key] === lookup.value;
      });

    });
  };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM