Lo-Dash Essentials
上QQ阅读APP看书,第一时间看更新

Sorting data

In Vanilla JavaScript, the approach to sorting involves arrays and two methods. The sort() method sorts the array in ascending order, using primitive comparison operations between the items. You can customize this behavior by passing sort(), a comparator function. For example, you use this callback function to sort an array in descending order. The other method, reverse(), simply reverses the order of the array. It's the inverse of the current order, whatever that might be.

The native array sort() method sorts the array in-place although you might not want that to happen. Immutable operations reduce side effects because they don't change the original collection. Specifically, you might have requested the API data in a specific order. A region of the UI wants to render this array in a different sort order. Well, you don't want to change the order from what was requested. In this case, it would be better to have a function that returns a new array that contains the items of the original, but in the expected sort order.

Using sortBy()

The sortBy() function is the Lo-Dash answer to the native Array.sort() method. Since it's an abstract collection function, it's not limited to arrays. Take a look at the following code:

_.sortBy('cba').join('');

While the function works just fine with strings as the input, the output is a sorted array of characters; hence, the call to join them back together. This is because the sortBy()function always returns an array as the result.

The sortBy() function is similar to the native Array.sort() method, in that it sorts collection items in ascending order by default. Also, similar to the native method, we can pass in a callback function to sortBy() that'll customize the sorting behavior, as follows:

var collection = [
    { name: 'Moe' },
    { name: 'Seymour' },
    { name: 'Harold' }, 
    { name: 'Willie' }
];

_.sortBy(collection, function(item) {
    return item.name;
});

The preceding callback function passed to sortBy() returns the value of an object property. By doing this, the sorting behavior will compare the property values—in this case, name—instead of the objects themselves. There's actually a shorter way to achieve the same result:

_.sortBy(collection, 'name');

This is what's referred to as the pluck style shorthand in Lo-Dash terminology. We pass in the name of the property we want to sort the collection by. The value of this property is then plucked from each object in the collection. There's actually a pluck() function we'll look at in more depth later on.

The last trick sortBy() has up its sleeve takes the pluck shorthand to the next level and allows sorting by multiple property names, as shown in the following code:

var collection = [
    { name: 'Clancy', age: 43 },
    { name: 'Edna', age: 32 },
    { name: 'Lisa', age: 10 },
    { name: 'Philip', age: 10 }
];

_.sortBy(collection, [ 'age', 'name' ]);
// →
// [
//   { name: "Lisa", age: 10 },
//   { name: "Philip", age: 10 },
//   { name: "Edna", age: 32 },
//   { name: "Clancy", age: 43 }
// ]

The primary determinant of order here is the age property. If we specify a second property name, this is used to determine the order of elements that have the same primary sort order. It serves as a tie breaker. Here, there are two objects where age equals 10. Since the name property is the secondary sort order, this is how these two objects are sorted. Multiple sort properties is a typical use case in web applications, which would require us to write a surprisingly large amount of JavaScript to achieve, if not for this Lo-Dash utility.

Maintaining the sort order

Using the sortBy() function is a great tool for changing the sort order of an existing collection, especially if we don't want to permanently alter the default sort order of that collection. Other times, however, you'll want to permanently keep a collection sorted. Sometimes, this is actually done for us by the backend API that sends us collections in the form of JSON data.

In these situations, sorting is easy because you don't actually have to sort anything. It's already done. The challenge lies in maintaining the sort order. Because, sometimes elements get added to collections in real time. The naive approach to maintain sort order here would be to simply add the new element to the collection then resort it. The Lo-Dash alternative is to figure out the insertion point that will keep the current collection sort order intact. This is shown in the following code:

var collection = [ 
    'Carl',
    'Gary',
    'Luigi',
    'Otto'
];

var name = 'Luke';

collection.splice(_.sortedIndex(collection, name), 0, name);
// → 
// [
//   "Carl",
//   "Gary",
//   "Luigi",
//   "Luke",
//   "Otto"
// ]

The new name variable gets inserted into the second-last position. This is really the only function needed to maintain the order of a sorted collection. The same splice() array method is used to remove items from the collection, which doesn't disrupt the order. Adding new items is a challenge because of the search that takes place to figure out the insertion index. The sortedIndex() function does a binary search on the collection to figure out where the new item fits.