Underscore: sortBy() based on multiple attributes

后端 未结 11 1100
隐瞒了意图╮
隐瞒了意图╮ 2020-11-27 11:09

I am trying to sort an array with objects based on multiple attributes. I.e if the first attribute is the same between two objects a second attribute should be used to comap

相关标签:
11条回答
  • 2020-11-27 11:47

    sortBy says that it is a stable sort algorithm so you should be able to sort by your second property first, then sort again by your first property, like this:

    var sortedArray = _(patients).chain().sortBy(function(patient) {
        return patient[0].name;
    }).sortBy(function(patient) {
        return patient[0].roomNumber;
    }).value();
    

    When the second sortBy finds that John and Lisa have the same room number it will keep them in the order it found them, which the first sortBy set to "Lisa, John".

    0 讨论(0)
  • 2020-11-27 11:49

    Just return an array of properties you want to sort with:

    ES6 Syntax

    var sortedArray = _.sortBy(patients, patient => [patient[0].name, patient[1].roomNumber])
    

    ES5 Syntax

    var sortedArray = _.sortBy(patients, function(patient) { 
        return [patient[0].name, patient[1].roomNumber]
    })
    

    This does not have any side effects of converting a number to a string.

    0 讨论(0)
  • 2020-11-27 11:51

    Here's a hacky trick I sometimes use in these cases: combine the properties in such a way that the result will be sortable:

    var sortedArray = _.sortBy(patients, function(patient) {
      return [patient[0].roomNumber, patient[0].name].join("_");
    });
    

    However, as I said, that's pretty hacky. To do this properly you'd probably want to actually use the core JavaScript sort method:

    patients.sort(function(x, y) {
      var roomX = x[0].roomNumber;
      var roomY = y[0].roomNumber;
      if (roomX !== roomY) {
        return compare(roomX, roomY);
      }
      return compare(x[0].name, y[0].name);
    });
    
    // General comparison function for convenience
    function compare(x, y) {
      if (x === y) {
        return 0;
      }
      return x > y ? 1 : -1;
    }
    

    Of course, this will sort your array in place. If you want a sorted copy (like _.sortBy would give you), clone the array first:

    function sortOutOfPlace(sequence, sorter) {
      var copy = _.clone(sequence);
      copy.sort(sorter);
      return copy;
    }
    

    Out of boredom, I just wrote a general solution (to sort by any arbitrary number of keys) for this as well: have a look.

    0 讨论(0)
  • 2020-11-27 11:53

    Perhaps underscore.js or just Javascript engines are different now than when these answers were written, but I was able to solve this by just returning an array of the sort keys.

    var input = [];
    
    for (var i = 0; i < 20; ++i) {
      input.push({
        a: Math.round(100 * Math.random()),
        b: Math.round(3 * Math.random())
      })
    }
    
    var output = _.sortBy(input, function(o) {
      return [o.b, o.a];
    });
    
    // output is now sorted by b ascending, a ascending
    

    In action, please see this fiddle: https://jsfiddle.net/mikeular/xenu3u91/

    0 讨论(0)
  • 2020-11-27 11:55

    Simple Example from http://janetriley.net/2014/12/sort-on-multiple-keys-with-underscores-sortby.html (courtesy of @MikeDevenney)

    Code

    var FullySortedArray = _.sortBy(( _.sortBy(array, 'second')), 'first');
    

    With Your Data

    var FullySortedArray = _.sortBy(( _.sortBy(patients, 'roomNumber')), 'name');
    
    0 讨论(0)
  • 2020-11-27 11:56

    btw your initializer for patients is a bit weird, isn't it? why don't you initialize this variable as this -as a true array of objects-you can do it using _.flatten() and not as an array of arrays of single object, maybe it's typo issue):

    var patients = [
            {name: 'Omar', roomNumber: 3, bedNumber: 1},
            {name: 'John', roomNumber: 1, bedNumber: 1},
            {name: 'Chris', roomNumber: 2, bedNumber: 1},
            {name: 'Lisa', roomNumber: 1, bedNumber: 2},
            {name: 'Kiko', roomNumber: 1, bedNumber: 2}
            ];
    

    I sorted the list differently and add Kiko into Lisa's bed; just for fun and see what changes would be done...

    var sorted = _(patients).sortBy( 
                        function(patient){
                           return [patient.roomNumber, patient.bedNumber, patient.name];
                        });
    

    inspect sorted and you'll see this

    [
    {bedNumber: 1, name: "John", roomNumber: 1}, 
    {bedNumber: 2, name: "Kiko", roomNumber: 1}, 
    {bedNumber: 2, name: "Lisa", roomNumber: 1}, 
    {bedNumber: 1, name: "Chris", roomNumber: 2}, 
    {bedNumber: 1, name: "Omar", roomNumber: 3}
    ]
    

    so my answer is : use an array in your callback function this is quite similar to Dan Tao's answer, I just forget the join (maybe because I removed the array of arrays of unique item :))
    Using your data structure, then it would be :

    var sorted = _(patients).chain()
                            .flatten()
                            .sortBy( function(patient){
                                  return [patient.roomNumber, 
                                         patient.bedNumber, 
                                         patient.name];
                            })
                            .value();
    

    and a testload would be interesting...

    0 讨论(0)
提交回复
热议问题