How to sort an array of objects by multiple fields?

后端 未结 30 2365
北恋
北恋 2020-11-21 11:34

From this original question, how would I apply a sort on multiple fields?

Using this slightly adapted structure, how would I sort city (ascending) & then price (

相关标签:
30条回答
  • 2020-11-21 12:25

    This is a complete cheat but I think that it adds value to this question because it's basically a canned library function that you can use out-of-the box.

    If your code has access to lodash or a lodash compatible library like underscore then you can use the _.sortBy method. The snippet below is copied directly from the lodash documentation.

    The commented results in the examples looks like they return arrays of arrays but that's just showing the order and not the actual results which are an array of objects.

    var users = [
      { 'user': 'fred',   'age': 48 },
      { 'user': 'barney', 'age': 36 },
      { 'user': 'fred',   'age': 40 },
      { 'user': 'barney', 'age': 34 }
    ];
    
    _.sortBy(users, [function(o) { return o.user; }]);
     // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
    
    _.sortBy(users, ['user', 'age']);
    // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
    
    0 讨论(0)
  • 2020-11-21 12:26

    Here 'AffiliateDueDate' and 'Title' are columns, both are sorted in ascending order.

    array.sort(function(a, b) {
    
                   if (a.AffiliateDueDate > b.AffiliateDueDate ) return 1;
                   else if (a.AffiliateDueDate < b.AffiliateDueDate ) return -1;
                   else if (a.Title > b.Title ) return 1;
                   else if (a.Title < b.Title ) return -1;
                   else return 0;
                 })
    
    0 讨论(0)
  • 2020-11-21 12:27

    Another way

    var homes = [
        {"h_id":"3",
         "city":"Dallas",
         "state":"TX",
         "zip":"75201",
         "price":"162500"},
        {"h_id":"4",
         "city":"Bevery Hills",
         "state":"CA",
         "zip":"90210",
         "price":"319250"},
        {"h_id":"6",
         "city":"Dallas",
         "state":"TX",
         "zip":"75000",
         "price":"556699"},
        {"h_id":"5",
         "city":"New York",
         "state":"NY",
         "zip":"00010",
         "price":"962500"}
        ];
    function sortBy(ar) {
      return ar.sort((a, b) => a.city === b.city ?
          b.price.toString().localeCompare(a.price) :
          a.city.toString().localeCompare(b.city));
    }
    console.log(sortBy(homes));

    0 讨论(0)
  • 2020-11-21 12:28

    Here is a simple functional approach. Specify sort order using array. Prepend minus to specify descending order.

    var homes = [
        {"h_id":"3", "city":"Dallas", "state":"TX","zip":"75201","price":"162500"},
        {"h_id":"4","city":"Bevery Hills", "state":"CA", "zip":"90210", "price":"319250"},
        {"h_id":"6", "city":"Dallas", "state":"TX", "zip":"75000", "price":"556699"},
        {"h_id":"5", "city":"New York", "state":"NY", "zip":"00010", "price":"962500"}
        ];
    
    homes.sort(fieldSorter(['city', '-price']));
    // homes.sort(fieldSorter(['zip', '-state', 'price'])); // alternative
    
    function fieldSorter(fields) {
        return function (a, b) {
            return fields
                .map(function (o) {
                    var dir = 1;
                    if (o[0] === '-') {
                       dir = -1;
                       o=o.substring(1);
                    }
                    if (a[o] > b[o]) return dir;
                    if (a[o] < b[o]) return -(dir);
                    return 0;
                })
                .reduce(function firstNonZeroValue (p,n) {
                    return p ? p : n;
                }, 0);
        };
    }
    

    Edit: in ES6 it's even shorter!

    "use strict";
    const fieldSorter = (fields) => (a, b) => fields.map(o => {
        let dir = 1;
        if (o[0] === '-') { dir = -1; o=o.substring(1); }
        return a[o] > b[o] ? dir : a[o] < b[o] ? -(dir) : 0;
    }).reduce((p, n) => p ? p : n, 0);
    
    const homes = [{"h_id":"3", "city":"Dallas", "state":"TX","zip":"75201","price":162500},     {"h_id":"4","city":"Bevery Hills", "state":"CA", "zip":"90210", "price":319250},{"h_id":"6", "city":"Dallas", "state":"TX", "zip":"75000", "price":556699},{"h_id":"5", "city":"New York", "state":"NY", "zip":"00010", "price":962500}];
    const sortedHomes = homes.sort(fieldSorter(['state', '-price']));
    
    document.write('<pre>' + JSON.stringify(sortedHomes, null, '\t') + '</pre>')

    0 讨论(0)
  • 2020-11-21 12:29

    A multi dimensional sorting method, based on this answer:

    Update: Here is an "optimized" version. It does a lot more preprocessing and creates a comparison function for each sorting option beforehand. It might need more more memory (as it stores a function for each sorting option, but it should preform a bit better as it does not have to determine the correct settings during the comparison. I have not done any profiling though.

    var sort_by;
    
    (function() {
        // utility functions
        var default_cmp = function(a, b) {
                if (a == b) return 0;
                return a < b ? -1 : 1;
            },
            getCmpFunc = function(primer, reverse) {
                var dfc = default_cmp, // closer in scope
                    cmp = default_cmp;
                if (primer) {
                    cmp = function(a, b) {
                        return dfc(primer(a), primer(b));
                    };
                }
                if (reverse) {
                    return function(a, b) {
                        return -1 * cmp(a, b);
                    };
                }
                return cmp;
            };
    
        // actual implementation
        sort_by = function() {
            var fields = [],
                n_fields = arguments.length,
                field, name, reverse, cmp;
    
            // preprocess sorting options
            for (var i = 0; i < n_fields; i++) {
                field = arguments[i];
                if (typeof field === 'string') {
                    name = field;
                    cmp = default_cmp;
                }
                else {
                    name = field.name;
                    cmp = getCmpFunc(field.primer, field.reverse);
                }
                fields.push({
                    name: name,
                    cmp: cmp
                });
            }
    
            // final comparison function
            return function(A, B) {
                var a, b, name, result;
                for (var i = 0; i < n_fields; i++) {
                    result = 0;
                    field = fields[i];
                    name = field.name;
    
                    result = field.cmp(A[name], B[name]);
                    if (result !== 0) break;
                }
                return result;
            }
        }
    }());
    

    Example usage:

    homes.sort(sort_by('city', {name:'price', primer: parseInt, reverse: true}));
    

    DEMO


    Original function:

    var sort_by = function() {
       var fields = [].slice.call(arguments),
           n_fields = fields.length;
    
       return function(A,B) {
           var a, b, field, key, primer, reverse, result, i;
    
           for(i = 0; i < n_fields; i++) {
               result = 0;
               field = fields[i];
    
               key = typeof field === 'string' ? field : field.name;
    
               a = A[key];
               b = B[key];
    
               if (typeof field.primer  !== 'undefined'){
                   a = field.primer(a);
                   b = field.primer(b);
               }
    
               reverse = (field.reverse) ? -1 : 1;
    
               if (a<b) result = reverse * -1;
               if (a>b) result = reverse * 1;
               if(result !== 0) break;
           }
           return result;
       }
    };
    

    DEMO

    0 讨论(0)
  • 2020-11-21 12:29

    I was looking for something similar and ended up with this:

    First we have one or more sorting functions, always returning either 0, 1 or -1:

    const sortByTitle = (a, b): number => 
      a.title === b.title ? 0 : a.title > b.title ? 1 : -1;
    

    You can create more functions for each other property you want to sort on.

    Then I have a function that combines these sorting functions into one:

    const createSorter = (...sorters) => (a, b) =>
      sorters.reduce(
        (d, fn) => (d === 0 ? fn(a, b) : d),
        0
      );
    

    This can be used to combine the above sorting functions in a readable way:

    const sorter = createSorter(sortByTitle, sortByYear)
    
    items.sort(sorter)
    

    When a sorting function returns 0 the next sorting function will be called for further sorting.

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