How to sort an array of objects by multiple fields?

后端 未结 30 2366
北恋
北恋 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:12

    Just another option. Consider to use the following utility function:

    /** Performs comparing of two items by specified properties
     * @param  {Array} props for sorting ['name'], ['value', 'city'], ['-date']
     * to set descending order on object property just add '-' at the begining of property
     */
    export const compareBy = (...props) => (a, b) => {
      for (let i = 0; i < props.length; i++) {
        const ascValue = props[i].startsWith('-') ? -1 : 1;
        const prop = props[i].startsWith('-') ? props[i].substr(1) : props[i];
        if (a[prop] !== b[prop]) {
          return a[prop] > b[prop] ? ascValue : -ascValue;
        }
      }
      return 0;
    };
    

    Example of usage (in your case):

    homes.sort(compareBy('city', '-price'));
    

    It should be noted that this function can be even more generalized in order to be able to use nested properties like 'address.city' or 'style.size.width' etc.

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

    I made a quite generic multi feature sorter today. You can have a look at thenBy.js here: https://github.com/Teun/thenBy.js

    It allows you to use the standard Array.sort, but with firstBy().thenBy().thenBy() style. It is way less code and complexity than the solutions posted above.

    0 讨论(0)
  • 2020-11-21 12:13
    function sortMultiFields(prop){
        return function(a,b){
            for(i=0;i<prop.length;i++)
            {
                var reg = /^\d+$/;
                var x=1;
                var field1=prop[i];
                if(prop[i].indexOf("-")==0)
                {
                    field1=prop[i].substr(1,prop[i].length);
                    x=-x;
                }
    
                if(reg.test(a[field1]))
                {
                    a[field1]=parseFloat(a[field1]);
                    b[field1]=parseFloat(b[field1]);
                }
                if( a[field1] > b[field1])
                    return x;
                else if(a[field1] < b[field1])
                    return -x;
            }
        }
    }
    

    How to use (put -(minus) sign before field if you want to sort in descending order particular field)

    homes.sort(sortMultiFields(["city","-price"]));
    

    Using above function you can sort any json array with multiple fields. No need to change function body at all

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

    Adaptation of @chriskelly 's answer.


    Most answers overlook that price will not sort properly if the value is in the ten thousands and lower or over a million. The resaon being JS sorts alphabetically. It was answered pretty well here, Why can't JavaScript sort "5, 10, 1" and here How to sort an array of integers correctly.

    Ultimately we have to do some evaluation if the field or node we're sorting by is an number. I am not saying that using parseInt() in this case is the correct answer, the sorted results are more important.

    var homes = [{
      "h_id": "2",
      "city": "Dallas",
      "state": "TX",
      "zip": "75201",
      "price": "62500"
    }, {
      "h_id": "1",
      "city": "Dallas",
      "state": "TX",
      "zip": "75201",
      "price": "62510"
    }, {
      "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(['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 (!parseInt(a[o]) && !parseInt(b[o])) {
              if (a[o] > b[o]) return dir;
              if (a[o] < b[o]) return -(dir);
              return 0;
            } else {
              return dir > 0 ? a[o] - b[o] : b[o] - a[o];
            }
          })
          .reduce(function firstNonZeroValue(p, n) {
            return p ? p : n;
          }, 0);
      };
    }
    document.getElementById("output").innerHTML = '<pre>' + JSON.stringify(homes, null, '\t') + '</pre>';
    <div id="output">
    
    </div>


    A fiddle to test with

    0 讨论(0)
  • 2020-11-21 12:17
    homes.sort(function(a,b) { return a.city - b.city } );
    homes.sort(function(a,b){
        if (a.city==b.city){
            return parseFloat(b.price) - parseFloat(a.price);
        } else {
            return 0;
        }
    });
    
    0 讨论(0)
  • 2020-11-21 12:20

    You could use a chained sorting approach by taking the delta of values until it reaches a value not equal to zero.

    var data = [{ 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" }];
    
    data.sort(function (a, b) {
        return a.city.localeCompare(b.city) || b.price - a.price;
    });
    
    console.log(data);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    Or, using es6, simply:

    data.sort((a, b) => a.city.localeCompare(b.city) || b.price - a.price);
    
    0 讨论(0)
提交回复
热议问题