How to sort an array of objects by multiple fields?

后端 未结 30 2363
北恋
北恋 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:06
    function sort(data, orderBy) {
            orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
            return data.sort((a, b) => {
                for (let i = 0, size = orderBy.length; i < size; i++) {
                    const key = Object.keys(orderBy[i])[0],
                        o = orderBy[i][key],
                        valueA = a[key],
                        valueB = b[key];
                    if (!(valueA || valueB)) {
                        console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
                        return [];
                    }
                    if (+valueA === +valueA) {
                        return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
                    } else {
                        if (valueA.localeCompare(valueB) > 0) {
                            return o.toLowerCase() === 'desc' ? -1 : 1;
                        } else if (valueA.localeCompare(valueB) < 0) {
                            return o.toLowerCase() === 'desc' ? 1 : -1;
                        }
                    }
                }
            });
        }
    

    Using :

    sort(homes, [{city : 'asc'}, {price: 'desc'}])
    

    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 sort(data, orderBy) {
                orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
                return data.sort((a, b) => {
                    for (let i = 0, size = orderBy.length; i < size; i++) {
                        const key = Object.keys(orderBy[i])[0],
                            o = orderBy[i][key],
                            valueA = a[key],
                            valueB = b[key];
                        if (!(valueA || valueB)) {
                            console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
                            return [];
                        }
                        if (+valueA === +valueA) {
                            return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
                        } else {
                            if (valueA.localeCompare(valueB) > 0) {
                                return o.toLowerCase() === 'desc' ? -1 : 1;
                            } else if (valueA.localeCompare(valueB) < 0) {
                                return o.toLowerCase() === 'desc' ? 1 : -1;
                            }
                        }
                    }
                });
            }
    console.log(sort(homes, [{city : 'asc'}, {price: 'desc'}]));

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

    The following function will allow you to sort an array of objects on one or multiple properties, either ascending (default) or descending on each property, and allow you to choose whether or not to perform case sensitive comparisons. By default, this function performs case insensitive sorts.

    The first argument must be the array containing the objects. The subsequent argument(s) must be a comma separated list of strings that reference the different object properties to sort by. The last argument (which is optional) is a boolean to choose whether or not to perform case sensitive sorts - use true for case sensitive sorts.

    The function will sort each property/key in ascending order by default. If you want a particular key to sort in descending order, then instead pass in an array in this format: ['property_name', true].

    Here are some sample uses of the function followed by an explanation (where homes is an array containing the objects):

    objSort(homes, 'city') --> sort by city (ascending, case in-sensitive)

    objSort(homes, ['city', true]) --> sort by city (descending, case in-sensitive)

    objSort(homes, 'city', true) --> sort by city then price (ascending, case sensitive)

    objSort(homes, 'city', 'price') --> sort by city then price (both ascending, case in-sensitive)

    objSort(homes, 'city', ['price', true]) --> sort by city (ascending) then price (descending), case in-sensitive)

    And without further ado, here's the function:

    function objSort() {
        var args = arguments,
            array = args[0],
            case_sensitive, keys_length, key, desc, a, b, i;
    
        if (typeof arguments[arguments.length - 1] === 'boolean') {
            case_sensitive = arguments[arguments.length - 1];
            keys_length = arguments.length - 1;
        } else {
            case_sensitive = false;
            keys_length = arguments.length;
        }
    
        return array.sort(function (obj1, obj2) {
            for (i = 1; i < keys_length; i++) {
                key = args[i];
                if (typeof key !== 'string') {
                    desc = key[1];
                    key = key[0];
                    a = obj1[args[i][0]];
                    b = obj2[args[i][0]];
                } else {
                    desc = false;
                    a = obj1[args[i]];
                    b = obj2[args[i]];
                }
    
                if (case_sensitive === false && typeof a === 'string') {
                    a = a.toLowerCase();
                    b = b.toLowerCase();
                }
    
                if (! desc) {
                    if (a < b) return -1;
                    if (a > b) return 1;
                } else {
                    if (a > b) return -1;
                    if (a < b) return 1;
                }
            }
            return 0;
        });
    } //end of objSort() function
    

    And here's some sample data:

    var homes = [{
        "h_id": "3",
        "city": "Dallas",
        "state": "TX",
        "zip": "75201",
        "price": 162500
    }, {
        "h_id": "4",
        "city": "Bevery Hills",
        "state": "CA",
        "zip": "90210",
        "price": 1000000
    }, {
        "h_id": "5",
        "city": "new york",
        "state": "NY",
        "zip": "00010",
        "price": 1000000
    }, {
        "h_id": "6",
        "city": "Dallas",
        "state": "TX",
        "zip": "85000",
        "price": 300000
    }, {
        "h_id": "7",
        "city": "New York",
        "state": "NY",
        "zip": "00020",
        "price": 345000
    }];
    
    0 讨论(0)
  • 2020-11-21 12:08

    Here's another one that's perhaps closer to your idea for the syntax

    function sortObjects(objArray, properties /*, primers*/) {
        var primers = arguments[2] || {}; // primers are optional
    
        properties = properties.map(function(prop) {
            if( !(prop instanceof Array) ) {
                prop = [prop, 'asc']
            }
            if( prop[1].toLowerCase() == 'desc' ) {
                prop[1] = -1;
            } else {
                prop[1] = 1;
            }
            return prop;
        });
    
        function valueCmp(x, y) {
            return x > y ? 1 : x < y ? -1 : 0; 
        }
    
        function arrayCmp(a, b) {
            var arr1 = [], arr2 = [];
            properties.forEach(function(prop) {
                var aValue = a[prop[0]],
                    bValue = b[prop[0]];
                if( typeof primers[prop[0]] != 'undefined' ) {
                    aValue = primers[prop[0]](aValue);
                    bValue = primers[prop[0]](bValue);
                }
                arr1.push( prop[1] * valueCmp(aValue, bValue) );
                arr2.push( prop[1] * valueCmp(bValue, aValue) );
            });
            return arr1 < arr2 ? -1 : 1;
        }
    
        objArray.sort(function(a, b) {
            return arrayCmp(a, b);
        });
    }
    
    // just for fun use this to reverse the city name when sorting
    function demoPrimer(str) {
        return str.split('').reverse().join('');
    }
    
    // Example
    sortObjects(homes, ['city', ['price', 'desc']], {city: demoPrimer});
    

    Demo: http://jsfiddle.net/Nq4dk/2/


    Edit: Just for fun, here's a variation that just takes an sql-like string, so you can do sortObjects(homes, "city, price desc")

    function sortObjects(objArray, properties /*, primers*/) {
        var primers = arguments[2] || {};
    
        properties = properties.split(/\s*,\s*/).map(function(prop) {
            prop = prop.match(/^([^\s]+)(\s*desc)?/i);
            if( prop[2] && prop[2].toLowerCase() === 'desc' ) {
                return [prop[1] , -1];
            } else {
                return [prop[1] , 1];
            }
        });
    
        function valueCmp(x, y) {
            return x > y ? 1 : x < y ? -1 : 0; 
        }
    
        function arrayCmp(a, b) {
            var arr1 = [], arr2 = [];
            properties.forEach(function(prop) {
                var aValue = a[prop[0]],
                    bValue = b[prop[0]];
                if( typeof primers[prop[0]] != 'undefined' ) {
                    aValue = primers[prop[0]](aValue);
                    bValue = primers[prop[0]](bValue);
                }
                arr1.push( prop[1] * valueCmp(aValue, bValue) );
                arr2.push( prop[1] * valueCmp(bValue, aValue) );
            });
            return arr1 < arr2 ? -1 : 1;
        }
    
        objArray.sort(function(a, b) {
            return arrayCmp(a, b);
        });
    }
    
    0 讨论(0)
  • 2020-11-21 12:08

    Here is mine for your reference, with example:

    function msort(arr, ...compFns) {
      let fn = compFns[0];
      arr = [].concat(arr);
      let arr1 = [];
      while (arr.length > 0) {
        let arr2 = arr.splice(0, 1);
        for (let i = arr.length; i > 0;) {
          if (fn(arr2[0], arr[--i]) === 0) {
            arr2 = arr2.concat(arr.splice(i, 1));
          }
        }
        arr1.push(arr2);
      }
    
      arr1.sort(function (a, b) {
        return fn(a[0], b[0]);
      });
    
      compFns = compFns.slice(1);
      let res = [];
      arr1.map(a1 => {
        if (compFns.length > 0) a1 = msort(a1, ...compFns);
        a1.map(a2 => res.push(a2));
      });
      return res;
    }
    
    let tstArr = [{ id: 1, sex: 'o' }, { id: 2, sex: 'm' }, { id: 3, sex: 'm' }, { id: 4, sex: 'f' }, { id: 5, sex: 'm' }, { id: 6, sex: 'o' }, { id: 7, sex: 'f' }];
    
    function tstFn1(a, b) {
      if (a.sex > b.sex) return 1;
      else if (a.sex < b.sex) return -1;
      return 0;
    }
    
    function tstFn2(a, b) {
      if (a.id > b.id) return -1;
      else if (a.id < b.id) return 1;
      return 0;
    }
    
    console.log(JSON.stringify(msort(tstArr, tstFn1, tstFn2)));
    //output:
    //[{"id":7,"sex":"f"},{"id":4,"sex":"f"},{"id":5,"sex":"m"},{"id":3,"sex":"m"},{"id":2,"sex":"m"},{"id":6,"sex":"o"},{"id":1,"sex":"o"}]
    
    0 讨论(0)
  • 2020-11-21 12:11

    A dynamic way to do that with MULTIPLE keys:

    • filter unique values from each col/key of sort
    • put in order or reverse it
    • add weights width zeropad for each object based on indexOf(value) keys values
    • sort using caclutated weights

    Object.defineProperty(Array.prototype, 'orderBy', {
    value: function(sorts) { 
        sorts.map(sort => {            
            sort.uniques = Array.from(
                new Set(this.map(obj => obj[sort.key]))
            );
    
            sort.uniques = sort.uniques.sort((a, b) => {
                if (typeof a == 'string') {
                    return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
                }
                else if (typeof a == 'number') {
                    return sort.inverse ? (a < b) : (a > b ? 1 : 0);
                }
                else if (typeof a == 'boolean') {
                    let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                    return x;
                }
                return 0;
            });
        });
    
        const weightOfObject = (obj) => {
            let weight = "";
            sorts.map(sort => {
                let zeropad = `${sort.uniques.length}`.length;
                weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
            });
            //obj.weight = weight; // if you need to see weights
            return weight;
        }
    
        this.sort((a, b) => {
            return weightOfObject(a).localeCompare( weightOfObject(b) );
        });
    
        return this;
    }
    });
    

    Use:

    // works with string, number and boolean
    let sortered = your_array.orderBy([
        {key: "type", inverse: false}, 
        {key: "title", inverse: false},
        {key: "spot", inverse: false},
        {key: "internal", inverse: true}
    ]);
    

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

    I think this may be the easiest way to do it.

    https://coderwall.com/p/ebqhca/javascript-sort-by-two-fields

    It's really simple and I tried it with 3 different key value pairs and it worked great.

    Here is a simple example, look at the link for more details

    testSort(data) {
        return data.sort(
            a['nameOne'] > b['nameOne'] ? 1
            : b['nameOne'] > a['nameOne'] ? -1 : 0 ||
            a['date'] > b['date'] ||
            a['number'] - b['number']
        );
    }
    
    0 讨论(0)
提交回复
热议问题