Efficient way to search a multi-dimensional array for key based on value

I have an array that looks similar to this:

arr['thisKey'] = { prop1 : 'a', prop2 : 'b', prop3 : 'c' }
arr['thatKey'] = { prop1 : 'x', prop2 : 'y', prop3 : 'z' }
arr['otherKey'] = { prop1 : 'red', prop2 : 'orange', prop3 : 'blue' }

I am trying to write a function (that is fast and efficient) that will return the key (or array of keys if there are more than 1) of the outer-most array based on a key/value pair nested within. Something like:

function getKey(theArray, prop, val) {
   // do some stuff
   return key;
}

var myKey = getKey(arr, 'prop2', 'orange');

The value of myKey should be “otherKey”.

I’m pretty sure this can be done with a couple of nested for loops but these arrays are rather large and I’m thinking that, especially using jquery’s grep(), there has to be a better way… or maybe not - I’m just stumped at the moment.

Any insight would be greatly appreciated!!

One idea would be to create a look up for the objects. This is particularly useful if you have to search for a value multiple times and the values are simple things like strings AND you are looking for an exact match. Partial matches will be more difficult to achieve.


function reverseProperties(obj) {
    var newObj = {};
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop) &&
            typeof(obj[prop]) === "string") {
            if (!newObj.hasOwnProperty(obj[prop])) {
                newObj[obj[prop]] = [];
            }
            newObj[obj[prop]].push(prop);
        }
    }
    return newObj;
}

var arr = {};
arr['thisKey'] = { prop1 : 'a', prop2 : 'b', prop3 : 'c' };
arr['thatKey'] = { prop1 : 'x', prop2 : 'y', prop3 : 'z' };
arr['otherKey'] = { prop1 : 'red', prop2 : 'orange', prop3 : 'blue' };

var reversed = reverseProperties(arr['thisKey']);

// Then to access simply use:

console.log(reversed['a']);
//or
console.log(reversed.a);

You will, of course, have to test whether the property (value) exists before using it. You might want to write another helper function for that:


function getProperty(obj,propName) {
   if (obj.hasOwnProperty(propName)) {
        return obj[propName];
   }
   return null;
}

var properties = getProperty(reversed,'a');

To make things neater you could think about putting these functions in a namespace.

Thanks for the response. If I’m reading this properly, it looks like I have to pass the function the main array with a key name. In this case, arr[‘thisKey’]. But I am actually trying to get the function to return ‘thisKey’, based on me telling it that prop1 = a.

Am I doing something wrong?

I’ve quickly run up the following code which does the job. It returns an array with all of the keys that contain the key/value that you ask for.
It makes good use of built-in Array methods that exist in most web browsers now. The pages for the filter and [url=“https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some”]some have compatibility code for if you want to use them on less modern browsers.


function filterKeys(obj, func) {
    return Array.prototype.filter.call(Object.keys(obj), func, obj);
}
function someKeys(obj, func) {
    return Array.prototype.some.call(Object.keys(obj), func, obj);
}
function atLeastOnePropertyMatches(obj, requiredProp) {
    return someKeys(obj, function (prop) {
        if (requiredProp.hasOwnProperty(prop)) {
            return this[prop] === requiredProp[prop];
        }
    });
}
function getMatchingKeys(obj, requiredProp) {
    return filterKeys(obj, function (prop) {
        return atLeastOnePropertyMatches(this[prop], required);
    });
}
var arr = {
    thisKey: {prop1: 'a', prop2: 'b', prop3: 'c'},
    thatKey: {prop1: 'x', prop2: 'y', prop3: 'z'},
    otherKey: {prop1: 'red', prop2: 'orange', prop3: 'blue'}
},
    required = {prop2: 'orange'};
    results = [];

results = getMatchingKeys(arr, required);
// results is now ['otherkey']

If you just want the name of the first matching property to be returned, you could instead return just the first matching key that’s found.


function firstMatchingKey(arr, requiredProp) {
    var matches = forEachKey(arr, Array.prototype.filter, function (prop) {
        return atLeastOnePropertyMatches(this[prop], required);
    });
    return matches[0];
}
...
result = firstMatchingKey(arr, required);
// result is now 'otherkey'

fantastic!! Thx!