Monday, March 31, 2014

Finding Keys By Value With Lodash

The Lodash library has a findKey() function that returns the first property name for which the given value is found. The where and pluck style callbacks are supported, as with most lodash functions of this kind. As is passing in our own callback to determine whether the value has been found or not. This is where we can handle some rather subtle use cases. For instance, what happens when one of the properties you're checking against is an array of objects. Meanwhile, one of those objects has the value you're looking for.

Using your own callback function, we can perform these sorts of comparisons. Consider the following data sample.

var data = {
    one: [
  { attr1: 'val1', attr2: 'val2' },
  { attr3: 'val3', attr4: 'val4' }
 ],
 two: { attr5: 'val5' },
 three: { attr6: 'val6' }
};

The two and three properties are straightforward to search — they're just simple objects that either contain the value we're looking for, or not. But in the case of property one, things get tricky because we need to traverse down into the array to see which objects meet our criteria. Here's an example of one such approach.

var value = 'val3';

_.findKey( data, function( item ) {
    if ( _.isArray( item ) ) {
        return _( item )
            .map(function( obj ) {
                return _.values( obj );    
            })
            .flatten()
            .uniq()
            .contains( value );
    } else if ( _.isObject( item ) ) {
        return _( item )
            .values()
            .uniq()
            .contains( value );
    }
});

Here we're passing in a function to findKey(), along with our sample data. The first thing we're looking for is whether or not the current item is an array. If so, we wrap the array in a Lodash object, so it's chainable, then we map it. We're mapping each object to an array of values in that object. Remember, the objective is to find the property name in the root object — not the objects nested within. Once the mapping is finished, we have an array of arrays.

The next step in the chain is to flatten the array, so there's no nesting. The following step is a simple removal of duplicate values using the uniq() function. And with that, we have our search space. We can now return the result of the contains() function to determine whether or not the property, and it's array value, contain the value we're looking for somewhere within. In this case, it does, since we're looking for 'val1'. Note too, that this approach to searching objects only goes one level down in the hierarchy, and that this will often suffice in most situations.