Sort array of objects by value using underscore.js

2019-07-01 17:56发布

问题:

I'm trying to sort an array of objects by the average property in descending order - so the largest average is first - but am not able to using underscore.js. Below is my attempt:

var jsonData = [
{
    "title": "Dear Kitten",
    "totalCount": 1689,
    "average": 241
},
{
    "title": "Weird Things All Couples Fight About",
    "totalCount": 9966,
    "average": 1424
},
{
    "title": "If Disney Princesses Were Real",
    "totalCount": 16567,
    "average": 2367
},
{
    "title": "Secret Tricks With Everyday Objects",
    "totalCount": 24884,
    "average": 3555
},
{
    "title": "The Coolest Travel Hacks",
    "totalCount": 41847,
    "average": 8369
},
{
    "title": "5 Ways You're Drinking Coffee Wrong",
    "totalCount": 55673,
    "average": 7953
},
{
    "title": "The Perfect Way To Pour A Beer",
    "totalCount": 58097,
    "average": 58097
},
{
    "title": "Fruit You're Eating Wrong",
    "totalCount": 65570,
    "average": 32785
},
{
    "title": "Your Cat Is Judging You",
    "totalCount": 78952,
    "average": 11279
},
{
    "title": "3rd Date vs 30th Date",
    "totalCount": 84394,
    "average": 14066
}
];

console.log(_.sortBy(jsonData, "average"));

jsFiddle

回答1:

The issue here is that you wanted the array to be sorted in descending order by average, instead of the default ascending order.

You could do this by providing a custom iteratee to the _.sortBy() function:

_.sortBy( jsonData, function( item ) { return -item.average; } )

Updated fiddle

But I don't recommend that. It would be much better to simply use the native JavaScript [].sort() method and provide it a comparison function:

jsonData.sort( function( a, b ) { return b.average - a.average; } )

Better fiddle

If you were sorting a very large array, this would also be faster than using _.sortBy(). Look at the source code for _.sortBy() to see why:

_.sortBy = function(obj, iteratee, context) {
  iteratee = cb(iteratee, context);
  return _.pluck(_.map(obj, function(value, index, list) {
    return {
      value: value,
      index: index,
      criteria: iteratee(value, index, list)
    };
  }).sort(function(left, right) {
    var a = left.criteria;
    var b = right.criteria;
    if (a !== b) {
      if (a > b || a === void 0) return 1;
      if (a < b || b === void 0) return -1;
    }
    return left.index - right.index;
  }), 'value');
};

It's doing quite a bit of work in addition to the .sort() call - and this code is just the tip of the iceberg, the helper functions it calls like cb() do a lot of work too.

Why do all that when it's just as easy to call .sort() directly yourself?

Also, it takes a close reading of that lengthy .sortBy() source to be sure that it does a numeric sort instead of a lexicographic sort - and the documentation doesn't say!

A lexicographic sort (aka alphabetic sort) is where the values are sorted as strings, not as numbers. So for example it would use this order:

[ 1424, 2367, 241, ... ]

When you call the native array .sort() yourself, you can easily verify that it uses a numeric sort: the value b.average - a.average is always a number.