Searching for data

Applications don't use entire collections. Rather, they iterate over a collection subset, or they look for a specific item in the collection. Lo-Dash has a number of functional tools to help the programmer find the data they need.

Filtering collections

The simplest way to perform a filter operation on a collection using Lo-Dash is to use the where() function. This function takes an object argument and will match its properties against each item in the collection, as shown in the following code:

var collection = [ 
    { name: 'Moe', age: 47, gender: 'm' },
    { name: 'Sarah', age: 32, gender: 'f' },
    { name: 'Melissa', age: 32, gender: 'f' },
    { name: 'Dave', age: 32, gender: 'm' }
];

_.where(collection, { age: 32, gender: 'f' });
// →
// [
//   { name: "Sarah", age: 32, gender: "f" },
//   { name: "Melissa", age: 32, gender: "f" }
// ]

The preceding code filters the collection on both the age and the gender properties. The query translates to thirty-two year old females. The Moe object matches with neither property, while the Dave object matches with the age property, but not gender. A good way to think about where() filtering is that each object property you pass in as the filter will be logical and joined together. For example, match the age and the gender properties.

The where() function is great for its concise syntax and intuitive application to collections. With this simplicity comes a few limitations. First, the property values that we're comparing to each item in the collection must match exactly. Sometimes, we need comparisons a little more exotic than strict equality. Second, the logical and way that where() joins query conditions together isn't always desirable. Logical or conditions are just as common.

For these types of advanced filtering capabilities, you should turn to the filter() function. Here's a basic filter operation that's even simpler than the where() queries:

var collection = [ 
    { name: 'Sean', enabled: false },
    { name: 'Joel', enabled: true },
    { name: 'Sue', enabled: false },
    { name: 'Jackie', enabled: true }
];

_.filter(collection, 'enabled');
// →
// [
//   { name: "Joel", enabled: true },
//   { name: "Jackie", enabled: true }
// ]

Since the enabled property has truthy values for two objects in this collection, they're returned in a new array.

Note

Lo-Dash uses the notion of truthy values everywhere. This simply means that a value will test positive if used in an if statement or a ternary operator. Values don't need to be of Boolean type and true to be truthy. An object, an array, a string, a number—these are all truthy values. Whereas null, undefined, and 0— are all false.

As mentioned, the filter() function fills gaps in the where() function. Unlike where(),filter() accepts a callback function that's applied to each item in the collection, as shown in the following code:

var collection = [ 
    { type: 'shirt', size: 'L' },
    { type: 'pants', size: 'S' },
    { type: 'shirt', size: 'XL' },
    { type: 'pants', size: 'M' }  
];

_.filter(collection, function(item) {
    return item.size === 'L' || item.size === 'M';
});
// →
// [
//   { type: "shirt", size: "L" },
//   { type: "pants", size: "M" }
// ]

The callback function uses an or condition to satisfy the size constraint here—medium or large. This is simply not doable with the where function.

Note

The filter() function accepts an object argument as well. In Lo-Dash terminology, this is called a where style callback. There are many functions, not just filter(), that accept the filter criteria specified as an object and behave like where().

Filtering collections using the filter() function is good when we know what we're looking for. The callback function gives the programmer enough flexibility to compose elaborate criteria. But sometimes, we don't know what you need from a collection. Instead, you only know what you don't need, as shown in the following code:

var collection = [
    { name: 'Ryan', enabled: true },
    { name: 'Megan', enabled: false },
    { name: 'Trevor', enabled: false },
    { name: 'Patricia', enabled: true }
];

_.reject(collection, { enabled: false });
// →
// [
//   { name: "Ryan", enabled: true },
//   { name: "Patricia", enabled: true }
// ]

You can see here that only enabled items are returned that are equivalent to doing _.filter(collection, {enabled: true}),which is a simple inversion of filter(). Which function you use is a matter of personal preference and the context in which they're used. Go for the one that reads cleaner in your code.

Note

reject() actually uses the filter() function internally. It uses the negate() function to invert the result of the callback passed to filter().

Finding items in collections

Sometimes, we need a specific collection item. Filtering a collection simply generates a new collection with less items in it. Conversely, finding items in a collection means finding a specific item.

The function used to find items in a collection is aptly named find(). This function accepts the same arguments as the filter() function. You can pass the name of the property as a string, an object filled with property names and values to execute a where style search, or just a plain callback function to match against whatever you want. The following is an example of this:

var collection = [ 
    { name: 'Derek', age: 37 },
    { name: 'Caroline', age: 35 },
    { name: 'Malcolm', age: 37 },
    { name: 'Hazel', age: 62 } 
];

_.find(collection, { age:37 });
// → { name: "Derek", age: 37 }

There're actually two items matching this where style criteria in the collection—Derek and Malcolm. If we were to run this code though, we'd see that only Derek is returned. That's because find() returns as soon as a match is found. Collection order matters when searching for items in collections. It doesn't take into consideration duplicate values.

Let's look in the other direction and see what we find. Using the same collection and the same search criteria, you can search in the opposite direction:

_.findLast(collection, { age:37 });
// → { name: "Malcolm", age: 37 }

While find() searches for the first occurrence of the matching item in the collection, findLast() searches for the last occurrence. This is useful when we're working with sorted collections—you can better optimize your linear searches.

Note

While Lo-Dash heavily optimizes the while loops used when iterating over collections, searches executed using functions such as find() are linear. It's important to remember that it's up to the programmer using Lo-Dash to consider the performance implications of their unique application data. Lo-Dash functions are optimized for the generic common case, they're not going to magically make your code faster by virtue of using them. They're tools to assist the programmer to make incredibly high-performance code.