如何通过多个字段对对象进行排序?

发布于 2025-02-13 20:25:25 字数 1000 浏览 4 评论 0 原文

从这个原始问题,我如何在多个字段上应用排序?

使用这种略微适应的结构,我将如何对城市(上升)进行分类&然后价格(下降)?

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"}
    ];

我喜欢一个事实,而不是答案一般方法。我打算使用此代码的地方,我将不得不分类日期以及其他事情。 “序列”对象的能力似乎很方便,即使不是有点麻烦。

我试图构建此 a>成为一个不错的通用例子,但我没有太多运气。

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 (descending)?

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"}
    ];

I liked the fact than an answer was given which provided a general approach. Where I plan to use this code, I will have to sort dates as well as other things. The ability to "prime" the object seemed handy, if not a little cumbersome.

I've tried to build this answer into a nice generic example, but I'm not having much luck.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

苏大泽ㄣ 2025-02-20 20:25:25

您可以通过取出值的三角洲,直到达到不等于零的值来使用链式排序方法。

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; }

或者,使用ES6,简单:

data.sort((a, b) => a.city.localeCompare(b.city) || b.price - a.price);

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);
×眷恋的温暖 2025-02-20 20:25:25

对于非一般,简单的解决方案,解决您的确切问题:

homes.sort(
   function(a, b) {          
      if (a.city === b.city) {
         // Price is only important when cities are the same
         return b.price - a.price;
      }
      return a.city > b.city ? 1 : -1;
   });

for a non-generic, simple solution to your exact problem:

homes.sort(
   function(a, b) {          
      if (a.city === b.city) {
         // Price is only important when cities are the same
         return b.price - a.price;
      }
      return a.city > b.city ? 1 : -1;
   });
写给空气的情书 2025-02-20 20:25:25

这是一种简单的功能通用方法。使用数组指定排序订单。预先减去以指定降序。

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);
    };
}

编辑:在ES6中,它甚至更短!

"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>')

Here is a simple functional generic 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>')

夕嗳→ 2025-02-20 20:25:25

一种多维分排序方法,基于此答案

强>更新:这是一个“优化”版本。它会做更多的预处理,并事先为每个排序选项创建一个比较功能。它可能需要更多的内存(因为它为每个排序选项存储一个函数,但是它应该更好地预成形,因为它不必在比较过程中确定正确的设置。但是我没有进行任何分析。

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;
        }
    }
}());

示例用法:

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

demo


原始功能:

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;
   }
};

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

毁梦 2025-02-20 20:25:25

我今天做了一个相当通用的多功能分隔器。您可以在此处查看thenby.js: https://github.com/teun/teun/thenby.js < /a>

它允许您使用标准array.sort,但是使用firstby()。thenby()。thenby()样式。与上面发布的解决方案相比,它的代码和复杂性要少。

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.

谁对谁错谁最难过 2025-02-20 20:25:25

通过多个字段对对象进行排序:

homes.sort(function(left, right) {
    var city_order = left.city.localeCompare(right.city);
    var price_order = parseInt(left.price) - parseInt(right.price);
    return city_order || -price_order;
});

注意

  • 一个函数传递给阵列排序预计将返回负/零/阳性表明较少/相等/更大。
  • a.localecompare(b)普遍支持用于字符串,返回 - 返回 - 1,0,1如果 a&lt; b a == b a&gt; b
  • 扣除在数字字段上起作用,因为 a -b 给出 - ,0,+如果 a&lt; b a == b a&gt; b
  • || 在最后一行中给出 City 优先级,而不是 PRISE
  • 字段中否定订单
  • -price_order 在任何 -price_order || date_order;
  • date 与减法进行比较,因为 date数学转换为毫秒以来1970年。

    var date_order = new Date(left.date) - 新日期(right.date); 谨慎:date()返回字符串,并且IS freakishly不同与new Date()构造函数不同。

  • boolean 与减法相比,保证将True和False变为1和0(因此减法产生-1或0或1)。

    var goodness_order = boolean(left.is_good)-Boolean(right.is_good)
    在布尔上进行排序非常不寻常,我建议我与boolean()构造函数吸引注意,即使他们已经布尔人了。

To sort an array of objects by multiple fields:

homes.sort(function(left, right) {
    var city_order = left.city.localeCompare(right.city);
    var price_order = parseInt(left.price) - parseInt(right.price);
    return city_order || -price_order;
});

Notes

  • A function passed to array sort is expected to return negative/zero/positive to indicate less/equal/greater.
  • a.localeCompare(b) is universally supported for strings, and returns -1,0,1 if a<b,a==b,a>b.
  • Subtraction works on numeric fields, because a - b gives -,0,+ if a<b,a==b,a>b.
  • || in the last line gives city priority over price.
  • Negate to reverse order in any field, as in -price_order
  • Add new fields to the or-chain: return city_order || -price_order || date_order;
  • Date compare with subtraction, because date math converts to milliseconds since 1970.
    var date_order = new Date(left.date) - new Date(right.date); CAUTION: Date() returns a string, and is freakishly different from the new Date() constructor.
  • Boolean compare with subtraction, which is guaranteed to turn true and false to 1 and 0 (therefore the subtraction produces -1 or 0 or 1).
    var goodness_order = Boolean(left.is_good) - Boolean(right.is_good)
    Sorting on a boolean is unusual enough that I suggest drawing attention with the Boolean() constructor, even if they're already boolean.
咿呀咿呀哟 2025-02-20 20:25:25

这是一个完整的作弊,但我认为它为这个问题增加了价值,因为它基本上是一个罐头库功能,您可以使用户外框。

如果您的代码可以访问 lodash 或lodash兼容库(例如 underscore ),则可以使用_。Sortby方法。下面的摘要直接从 lodash文档

示例中评论的结果看起来像是返回数组的数组,但这只是显示顺序,而不是实际结果,即对象数组。

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]]

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]]
删除会话 2025-02-20 20:25:25

以下功能将允许您对一个或多个属性上的对象数组进行排序,无论是上升(默认)还是在每个属性上下降,并允许您选择是否执行案例敏感比较。默认情况下,此功能执行案例不敏感的方式。

第一个参数必须是包含对象的数组。
随后的参数必须是逗号分开的字符串列表,该列表将不同的对象属性进行排序。最后一个参数(是可选的)是选择是否执行案例敏感的情况 - 使用 true 对情况敏感。

该功能默认情况下将按升序排序每个属性/键。如果您想要一个特定的键来按降序排序,则应以这种格式传递数组: ['property_name',true]

以下是该功能的一些示例用途,然后是解释(其中 homes 是包含对象的数组):

objsort(home,'city'') - &gt;按城市排序(上升,敏感的案例)

objsort(房屋,['city',true]) - &gt;按城市(下降,案例对敏感)进行排序

objsort(房屋,'city',true) - &gt;然后按城市排序然后价格(上升,case 敏感

objsort(房屋,'city','price') - &gt;然后按城市进行分类,然后价格(既升级,案例对敏感)

objsort(房屋,'City',['Price',true],true]) - &gt;按城市(上升)和价格(下降),案例不敏感的)进行排序,

而无需进一步的ADO,这是功能:

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

这里是一些示例数据:

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
}];

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
}];
向地狱狂奔 2025-02-20 20:25:25

使用多个键来完成此操作的动态方法:

  • 从每个col/ofers to Coll of Cold
  • 或converse to
  • Coll of Colle/tovers forter toruts unique unique utvers unters utvers to and the Adgs witth width zeropad zeropad为每个对象基于indexof(value)键keys值
  • sort使用使用caclutated重量

“在此处输入图像描述”

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 ? b - a : a - b;
            }
            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;
}
});

使用:

// 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}
]);

“在此处输入图像说明”

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

enter image description here

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 ? b - a : a - b;
            }
            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}
]);

enter image description here

一场春暖 2025-02-20 20:25:25

这是一种通用的多维形式,可以在每个级别上进行逆转和/或映射。

用打字稿编写。对于JavaScript,请查看此 jsfiddle

代码

type itemMap = (n: any) => any;

interface SortConfig<T> {
  key: keyof T;
  reverse?: boolean;
  map?: itemMap;
}

export function byObjectValues<T extends object>(keys: ((keyof T) | SortConfig<T>)[]): (a: T, b: T) => 0 | 1 | -1 {
  return function(a: T, b: T) {
    const firstKey: keyof T | SortConfig<T> = keys[0];
    const isSimple = typeof firstKey === 'string';
    const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig<T>).key;
    const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig<T>).reverse;
    const map: itemMap | null = isSimple ? null : (firstKey as SortConfig<T>).map || null;

    const valA = map ? map(a[key]) : a[key];
    const valB = map ? map(b[key]) : b[key];
    if (valA === valB) {
      if (keys.length === 1) {
        return 0;
      }
      return byObjectValues<T>(keys.slice(1))(a, b);
    }
    if (reverse) {
      return valA > valB ? -1 : 1;
    }
    return valA > valB ? 1 : -1;
  };
}

用法用

姓氏和姓氏来分类一个人阵列的 代码示例:

interface Person {
  firstName: string;
  lastName: string;
}

people.sort(byObjectValues<Person>(['lastName','firstName']));

按其 name 对语言代码进行排序,而不是其语言代码(请参阅 MAP ),然后通过 discending 版本(请参阅 reververs )。

interface Language {
  code: string;
  version: number;
}

// languageCodeToName(code) is defined elsewhere in code

languageCodes.sort(byObjectValues<Language>([
  {
    key: 'code',
    map(code:string) => languageCodeToName(code),
  },
  {
    key: 'version',
    reverse: true,
  }
]));

Here's a generic multidimensional sort, allowing for reversing and/or mapping on each level.

Written in Typescript. For Javascript, check out this JSFiddle

The Code

type itemMap = (n: any) => any;

interface SortConfig<T> {
  key: keyof T;
  reverse?: boolean;
  map?: itemMap;
}

export function byObjectValues<T extends object>(keys: ((keyof T) | SortConfig<T>)[]): (a: T, b: T) => 0 | 1 | -1 {
  return function(a: T, b: T) {
    const firstKey: keyof T | SortConfig<T> = keys[0];
    const isSimple = typeof firstKey === 'string';
    const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig<T>).key;
    const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig<T>).reverse;
    const map: itemMap | null = isSimple ? null : (firstKey as SortConfig<T>).map || null;

    const valA = map ? map(a[key]) : a[key];
    const valB = map ? map(b[key]) : b[key];
    if (valA === valB) {
      if (keys.length === 1) {
        return 0;
      }
      return byObjectValues<T>(keys.slice(1))(a, b);
    }
    if (reverse) {
      return valA > valB ? -1 : 1;
    }
    return valA > valB ? 1 : -1;
  };
}

Usage Examples

Sorting a people array by last name, then first name:

interface Person {
  firstName: string;
  lastName: string;
}

people.sort(byObjectValues<Person>(['lastName','firstName']));

Sort language codes by their name, not their language code (see map), then by descending version (see reverse).

interface Language {
  code: string;
  version: number;
}

// languageCodeToName(code) is defined elsewhere in code

languageCodes.sort(byObjectValues<Language>([
  {
    key: 'code',
    map(code:string) => languageCodeToName(code),
  },
  {
    key: 'version',
    reverse: true,
  }
]));
放我走吧 2025-02-20 20:25:25

这是您对语法演示的想法可能更接近的

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});


编辑:只是为了娱乐,这是一个变体只需采用SQL-like字符串,因此您可以做 sortobjects(homes,homees,home,,''城市,价格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);
    });
}

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);
    });
}
Hello爱情风 2025-02-20 20:25:25

更简单一个:

var someArray = [...];

function generateSortFn(props) {
    return function (a, b) {
        for (var i = 0; i < props.length; i++) {
            var prop = props[i];
            var name = prop.name;
            var reverse = prop.reverse;
            if (a[name] < b[name])
                return reverse ? 1 : -1;
            if (a[name] > b[name])
                return reverse ? -1 : 1;
        }
        return 0;
    };
};

someArray.sort(generateSortFn([{name: 'prop1', reverse: true}, {name: 'prop2'}]));

Simpler one:

var someArray = [...];

function generateSortFn(props) {
    return function (a, b) {
        for (var i = 0; i < props.length; i++) {
            var prop = props[i];
            var name = prop.name;
            var reverse = prop.reverse;
            if (a[name] < b[name])
                return reverse ? 1 : -1;
            if (a[name] > b[name])
                return reverse ? -1 : 1;
        }
        return 0;
    };
};

someArray.sort(generateSortFn([{name: 'prop1', reverse: true}, {name: 'prop2'}]));
鲜肉鲜肉永远不皱 2025-02-20 20:25:25

为什么要复杂?只要对两次排序!这很好地工作:
(只需确保将重要性顺序从最少扭转到大多数):

jj.sort( (a, b) => (a.id >= b.id) ? 1 : -1 );
jj.sort( (a, b) => (a.status >= b.status) ? 1 : -1 );

why complicate? just sort it twice! this works perfectly:
(just make sure to reverse the importance order from least to most):

jj.sort( (a, b) => (a.id >= b.id) ? 1 : -1 );
jj.sort( (a, b) => (a.status >= b.status) ? 1 : -1 );
初吻给了烟 2025-02-20 20:25:25

我喜欢Snowburnt的方法,但需要进行调整才能测试与城市的等价相同的情况。

homes.sort(
   function(a,b){
      if (a.city==b.city){
         return (b.price-a.price);
      } else {
         return (a.city-b.city);
      }
   });

I like SnowBurnt's approach but it needs a tweak to test for equivalence on city NOT a difference.

homes.sort(
   function(a,b){
      if (a.city==b.city){
         return (b.price-a.price);
      } else {
         return (a.city-b.city);
      }
   });
戏剧牡丹亭 2025-02-20 20:25:25

为了使事情变得简单,请使用这些辅助功能。

您可以根据需要按任意数量的字段进行排序。对于每个排序字段,请指定属性名称,然后(可选)指定 -1 作为排序而不是上升的排序方向。

const 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"},
  {"h_id":"7","city":"New York","state":"NY","zip":"00010","price":"800500"}
]

const sortLexically   = (p,d=1)=>(a,b)=>d * a[p].localeCompare(b[p])
const sortNumerically = (p,d=1)=>(a,b)=>d * (a[p]-b[p])
const sortBy          = sorts=>(a,b)=>sorts.reduce((r,s)=>r||s(a,b),0)

// sort first by city, then by price descending
data.sort(sortBy([sortLexically('city'), sortNumerically('price', -1)]))

console.log(data)

To make things simple, use these helper functions.

You can sort by as many fields as you need. For each sort field, specify the property name, and then, optionally, specify -1 as the sort direction to sort descending instead of ascending.

const 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"},
  {"h_id":"7","city":"New York","state":"NY","zip":"00010","price":"800500"}
]

const sortLexically   = (p,d=1)=>(a,b)=>d * a[p].localeCompare(b[p])
const sortNumerically = (p,d=1)=>(a,b)=>d * (a[p]-b[p])
const sortBy          = sorts=>(a,b)=>sorts.reduce((r,s)=>r||s(a,b),0)

// sort first by city, then by price descending
data.sort(sortBy([sortLexically('city'), sortNumerically('price', -1)]))

console.log(data)

无畏 2025-02-20 20:25:25

这是我的解决方案,基于 schwartzian transform transform transform Idiom ,希望您发现它有用。

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

这是如何使用它的示例:

let games = [
  { name: 'Pako',              rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));

Here's my solution based on the Schwartzian transform idiom, hope you find it useful.

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

Here's an example how to use it:

let games = [
  { name: 'Pako',              rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));
享受孤独 2025-02-20 20:25:25

只是另一个选择。考虑使用以下实用程序函数:

/** 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;
};

用法的示例(在您的情况下):

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

应注意,此功能可以更具概括性,以便能够使用``address..city'''或'style.size等嵌套属性。宽度'等

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.

拍不死你 2025-02-20 20:25:25

可以通过添加3个相对简单的助手来制定一种非常直观的功能解决方案。在我们潜水之前,让我们从用法开始:

function usage(homes, { asc, desc, fallback }) {
  homes.sort(fallback(
    asc(home => home.city),
    desc(home => parseInt(home.price, 10)),
  ));
  console.log(homes);
}

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",
}];

const SortHelpers = (function () {
  const asc  = (fn) => (a, b) => (a = fn(a), b = fn(b), -(a < b) || +(a > b));
  const desc = (fn) => (a, b) => asc(fn)(b, a);
  const fallback = (...fns) => (a, b) => fns.reduce((diff, fn) => diff || fn(a, b), 0);
  return { asc, desc, fallback };
})();

usage(homes, SortHelpers);

如果您向下滚动片段,您可能已经看到了帮助者:

const asc  = (fn) => (a, b) => (a = fn(a), b = fn(b), -(a < b) || +(a > b));
const desc = (fn) => (a, b) => asc(fn)(b, a);
const fallback = (...fns) => (a, b) => fns.reduce((diff, fn) => diff || fn(a, b), 0);

让我快速解释这些功能中的每一个。

  • ASC 创建一个比较器函数。提供的功能 fn 均针对比较器参数 a b 均可调用。然后比较两个函数调用的结果。 -1 如果 Resurota&lt;如果 resupta&gt; resultb 0 否则。这些返回值对应于上升顺序方向。

    也可以像这样写:

     函数acc(fn){
      返回功能(a,b){
        //将``fn`都应用于`
        a = fn(a);
        b = fn(b);
    
        if(a&lt; b)返回-1;
        if(a&gt; b)返回1;
        返回0;
        //或`返回 - (a&lt; b)|| +(a&gt; b)`简称
      };
    }
     

  • desc 非常简单,因为它只是调用 asc ,但交换 a b 参数,导致降序而不是上升。

  • hallback (可能有一个更好的名称)允许我们使用单个 sort

    使用多个比较器功能

    asc desc 可以通过他们自己传递给 sort

      homes.sort(asc(home =&gt; home.city)))
     

    但是,如果要组合多个比较器功能,就存在问题。 Sort 仅接受单个比较器功能。 后备将多个比较器功能结合到一个比较器中。

    第一个比较器用参数调用 a b ,如果比较器返回值 0 (这意味着值相等)然后,我们回到下一个比较器。一直持续到找到非 0 值,或者在调用所有比较器之前,在这种情况下,返回值为 0

您也可以将自定义比较函数提供给 hallback()。假设您要使用 localecompare(),而不是将字符串与&lt; &gt; 进行比较。在这种情况下,您可以用 ASC(HOME =&GT; HOME.CITY)(a,b)=&gt; A.City.localecompare(B.City)

homes.sort(fallback(
  (a, b) => a.city.localeCompare(b.city),
  desc(home => parseInt(home.price, 10)),
));

要注意的一件事是,在与&lt; &gt; 。因此,如果可能缺少一个值,则可能需要先按其存在进行排序。

homes.sort(fallback(
  // homes with optionalProperty first, true (1) > false (0) so we use desc
  desc(home => home.optionalProperty != null), // checks for both null and undefined
  asc(home => home.optionalProperty),
  // ...
))

由于将字符串与 localecompare()进行比较是很普遍的事情,因此您可以将其作为 asc()的一部分。

function hasMethod(item, methodName) {
  return item != null && typeof item[methodName] === "function";
}

function asc(fn) {
  return function (a, b) {
    a = fn(a);
    b = fn(b);

    const areLocaleComparable =
      hasMethod(a, "localeCompare") && hasMethod(b, "localeCompare");

    if (areLocaleComparable) return a.localeCompare(b);

    return -(a < b) || +(a > b);
  };
}

A very intuitive functional solution can be crafted by adding 3 relatively simple helpers. Before we dive in, let's start with the usage:

function usage(homes, { asc, desc, fallback }) {
  homes.sort(fallback(
    asc(home => home.city),
    desc(home => parseInt(home.price, 10)),
  ));
  console.log(homes);
}

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",
}];

const SortHelpers = (function () {
  const asc  = (fn) => (a, b) => (a = fn(a), b = fn(b), -(a < b) || +(a > b));
  const desc = (fn) => (a, b) => asc(fn)(b, a);
  const fallback = (...fns) => (a, b) => fns.reduce((diff, fn) => diff || fn(a, b), 0);
  return { asc, desc, fallback };
})();

usage(homes, SortHelpers);

If you scrolled down the snippet you probably already saw the helpers:

const asc  = (fn) => (a, b) => (a = fn(a), b = fn(b), -(a < b) || +(a > b));
const desc = (fn) => (a, b) => asc(fn)(b, a);
const fallback = (...fns) => (a, b) => fns.reduce((diff, fn) => diff || fn(a, b), 0);

Let me quickly explain what each of these functions does.

  • asc creates a comparator function. The provided function fn is called for both the comparator arguments a and b. The results of the two function calls are then compared. -1 is returned if resultA < resultB, 1 is returned if resultA > resultB, or 0 otherwise. These return values correspond with an ascending order direction.

    It could also be written like this:

    function asc(fn) {
      return function (a, b) {
        // apply `fn` to both `a` and `b`
        a = fn(a);
        b = fn(b);
    
        if (a < b) return -1;
        if (a > b) return  1;
        return 0;
        // or `return -(a < b) || +(a > b)` for short
      };
    }
    
  • desc is super simple, since it just calls asc but swaps the a and b arguments, resulting in descending order instead of ascending.

  • fallback (there might be a better name for this) allows us to use multiple comparator functions with a single sort.

    Both asc and desc can be passed to sort by themself.

    homes.sort(asc(home => home.city))
    

    There is however an issue if you want to combine multiple comparator functions. sort only accepts a single comparator function. fallback combines multiple comparator functions into a single comparator.

    The first comparator is called with arguments a and b, if the comparator returns the value 0 (meaning that the values are equal) then we fall back to the next comparator. This continues until a non-0 value is found, or until all comparators are called, in which case the return value is 0.

You can provide your custom comparator functions to fallback() as well. Say you want to use localeCompare() instead of comparing strings with < and >. In such a case you can replace asc(home => home.city) with (a, b) => a.city.localeCompare(b.city).

homes.sort(fallback(
  (a, b) => a.city.localeCompare(b.city),
  desc(home => parseInt(home.price, 10)),
));

One thing to note is that values that can be undefined will always return false when comparing with < and >. So if a value can be missing you might want to sort by its presence first.

homes.sort(fallback(
  // homes with optionalProperty first, true (1) > false (0) so we use desc
  desc(home => home.optionalProperty != null), // checks for both null and undefined
  asc(home => home.optionalProperty),
  // ...
))

Since comparing strings with localeCompare() is such a common thing to do, you could include this as part of asc().

function hasMethod(item, methodName) {
  return item != null && typeof item[methodName] === "function";
}

function asc(fn) {
  return function (a, b) {
    a = fn(a);
    b = fn(b);

    const areLocaleComparable =
      hasMethod(a, "localeCompare") && hasMethod(b, "localeCompare");

    if (areLocaleComparable) return a.localeCompare(b);

    return -(a < b) || +(a > b);
  };
}
耳钉梦 2025-02-20 20:25:25

另一种方式

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));

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));

早茶月光 2025-02-20 20:25:25

只需遵循您的排序标准列表,

即使您有36个排序标准来包含

Nina此处提出的解决方案, 该代码始终保持可读性和可理解false在布尔逻辑中,布尔测试可以返回JavaScript中的其他内容(以下是数字值),这对于初学者来说总是令人困惑。

还要考虑谁需要维护您的代码。也许是你:想象一下自己花了几天的时间耙开另一个代码并有一个有害的虫子...而您读了这千行的充满技巧的人,您会筋疲力尽

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 fSort = (a,b) =>
  {
  let Dx = a.city.localeCompare(b.city)              // 1st criteria
  if (Dx===0) Dx = Number(b.price) - Number(a.price) // 2nd

  // if (Dx===0) Dx = ... // 3rd
  // if (Dx===0) Dx = ... // 4th....
  return Dx
  }

console.log( homes.sort(fSort))

simply follow the list of your sorting criteria

this code will always remain readable and understandable even if you have 36 sorting criteria to encase

The solution proposed here by Nina is certainly very elegant, but it implies knowing that a value of zero corresponds to a value of false in Boolean logic, and that Boolean tests can return something other than true / false in JavaScript (here are numeric values) which will always be confusing for a beginner.

Also think about who will need to maintain your code. Maybe it would be you: imagine yourself spending your days raking for days the code of another and having a pernicious bug ... and you are exhausted from reading these thousands of lines full of tips

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 fSort = (a,b) =>
  {
  let Dx = a.city.localeCompare(b.city)              // 1st criteria
  if (Dx===0) Dx = Number(b.price) - Number(a.price) // 2nd

  // if (Dx===0) Dx = ... // 3rd
  // if (Dx===0) Dx = ... // 4th....
  return Dx
  }

console.log( homes.sort(fSort))

帅哥哥的热头脑 2025-02-20 20:25:25

添加几个助手功能使您可以普遍解决此类问题。 sortbykey 采用一个数组和一个函数,该功能应返回与每个数组条目进行比较的项目列表。

这利用了JavaScript与 [2]&lt进行简单值数组的明智比较的事实。 [2,0]&lt; [2,1]&lt; [10,0]

// Two helpers:
function cmp(a, b) {
    if (a > b) {
        return 1
    } else if (a < b) {
        return -1
    } else {
        return 0
    }
}

function sortByKey(arr, key) {
    arr.sort((a, b) => cmp(key(a), key(b)))
}

// A demonstration:
let arr = [{a:1, b:2}, {b:3, a:0}, {a:1, b:1}, {a:2, b:2}, {a:2, b:1}, {a:1, b:10}]
sortByKey(arr, item => [item.a, item.b])

console.log(JSON.stringify(arr))
// '[{"b":3,"a":0},{"a":1,"b":1},{"a":1,"b":10},{"a":1,"b":2},{"a":2,"b":1},{"a":2,"b":2}]'

sortByKey(arr, item => [item.b, item.a])
console.log(JSON.stringify(arr))
// '[{"a":1,"b":1},{"a":2,"b":1},{"a":1,"b":10},{"a":1,"b":2},{"a":2,"b":2},{"b":3,"a":0}]'

我从python的

Adding a couple helper functions lets you solved this kind of problem generically and simply. sortByKey takes an array and a function which should return a list of items with which to compare each array entry.

This takes advantage of the fact that javascript does smart comparison of arrays of simple values, with [2] < [2, 0] < [2, 1] < [10, 0].

// Two helpers:
function cmp(a, b) {
    if (a > b) {
        return 1
    } else if (a < b) {
        return -1
    } else {
        return 0
    }
}

function sortByKey(arr, key) {
    arr.sort((a, b) => cmp(key(a), key(b)))
}

// A demonstration:
let arr = [{a:1, b:2}, {b:3, a:0}, {a:1, b:1}, {a:2, b:2}, {a:2, b:1}, {a:1, b:10}]
sortByKey(arr, item => [item.a, item.b])

console.log(JSON.stringify(arr))
// '[{"b":3,"a":0},{"a":1,"b":1},{"a":1,"b":10},{"a":1,"b":2},{"a":2,"b":1},{"a":2,"b":2}]'

sortByKey(arr, item => [item.b, item.a])
console.log(JSON.stringify(arr))
// '[{"a":1,"b":1},{"a":2,"b":1},{"a":1,"b":10},{"a":1,"b":2},{"a":2,"b":2},{"b":3,"a":0}]'

I've lovingly stolen this idea from Python's list.sort function.

失眠症患者 2025-02-20 20:25:25

我的谦虚提议:

function cmp(a, b) {
  if (a > b) return 1;
  if (a < b) return -1;
  return 0;
}

function objCmp(a, b, fields) {
  for (let field of fields) {
    let ret = 
      cmp(a[field], b[field]);
    if (ret != 0) {
      return ret;
    }
  }
  return 0;
}

通过这两个功能,您可以通过以下方式将一系列对象排序:

let sortedArray = homes.sort(
  (a, b) => objCmp(
    a, b, ['state', 'city'])
);

my humble proposal:

function cmp(a, b) {
  if (a > b) return 1;
  if (a < b) return -1;
  return 0;
}

function objCmp(a, b, fields) {
  for (let field of fields) {
    let ret = 
      cmp(a[field], b[field]);
    if (ret != 0) {
      return ret;
    }
  }
  return 0;
}

with these two functions, you may sort quite elegantly an array of objects the following way:

let sortedArray = homes.sort(
  (a, b) => objCmp(
    a, b, ['state', 'city'])
);
感性 2025-02-20 20:25:25

这是 @Snowburnt解决方案的通用版本:

var sortarray = [{field:'city', direction:'asc'}, {field:'price', direction:'desc'}];
array.sort(function(a,b){
    for(var i=0; i<sortarray.length; i++){
        retval = a[sortarray[i].field] < b[sortarray[i].field] ? -1 : a[sortarray[i].field] > b[sortarray[i].field] ? 1 : 0;
        if (sortarray[i].direction == "desc") {
            retval = retval * -1;
        }
        if (retval !== 0) {
            return retval;
        }
    }
}


})

这是基于我正在使用的排序例程。我没有测试此特定代码,因此可能会有错误,但是您明白了。这个想法是根据指示差异的第一个字段进行排序,然后停止并转到下一个记录。因此,如果您要按三个字段进行排序,并且比较中的第一个字段足以确定要排序的两个记录的排序顺序,然后返回该排序结果,然后转到下一个记录。

我在5000个记录上对其进行了测试(实际上具有更复杂的排序逻辑),并在眼睛的眨眼上进行了测试。如果您实际上将超过1000个记录加载到客户端,则可能应该使用严重侧排序和过滤。

该代码没有处理病例敏感性,但我将其留给读者来处理这种琐碎的修改。

Here is a generic version of @Snowburnt's solution:

var sortarray = [{field:'city', direction:'asc'}, {field:'price', direction:'desc'}];
array.sort(function(a,b){
    for(var i=0; i<sortarray.length; i++){
        retval = a[sortarray[i].field] < b[sortarray[i].field] ? -1 : a[sortarray[i].field] > b[sortarray[i].field] ? 1 : 0;
        if (sortarray[i].direction == "desc") {
            retval = retval * -1;
        }
        if (retval !== 0) {
            return retval;
        }
    }
}


})

This is based on a sort routine I'm using. I didn't test this specific code so it may have errors but you get the idea. The idea is to sort based on the first field that indicates a difference and then stop and go to the next record. So, if you're sorting by three fields and the first field in the compare is enough to determine the sort order of the two records being sorted then return that sort result and go to the next record.

I tested it (actually with a little more complex sort logic) on 5000 records and it did it in the blink of an eye. If you're actually loading more than 1000 records to the client you should probably be using sever-side sorting and filtering.

This code isn't handling case-sensitivity but I leave it to the reader to handle this trivial modification.

一绘本一梦想 2025-02-20 20:25:25
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;
        }
    }
}

如何使用(如果要在降序特定字段中排序特定字段)

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

使用上述功能,则可以在字段前使用(减去)符号。您可以将所有JSON数组与多个字段进行排序。 根本不需要改变功能主体

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

抚笙 2025-02-20 20:25:25
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;
                    }
                }
            }
        });
    }

使用 :

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'}]));

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'}]));

最后的乘客 2025-02-20 20:25:25
// custom sorting by city
const sortArray = ['Dallas', 'New York', 'Beverly Hills'];

const sortData = (sortBy) =>
  data
    .sort((a, b) => {
      const aIndex = sortBy.indexOf(a.city);
      const bIndex = sortBy.indexOf(b.city);

      if (aIndex < bIndex) {
        return -1;
      }

      if (aIndex === bIndex) {
        // price descending
        return b.price- a.price;
      }

      return 1;
    });

sortData(sortArray);
// custom sorting by city
const sortArray = ['Dallas', 'New York', 'Beverly Hills'];

const sortData = (sortBy) =>
  data
    .sort((a, b) => {
      const aIndex = sortBy.indexOf(a.city);
      const bIndex = sortBy.indexOf(b.city);

      if (aIndex < bIndex) {
        return -1;
      }

      if (aIndex === bIndex) {
        // price descending
        return b.price- a.price;
      }

      return 1;
    });

sortData(sortArray);
葬花如无物 2025-02-20 20:25:25

您可以使用lodash orderby函数 lodash

它需要两个段落的字段和方向阵列( “ ASC”,'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"}
    ];

var sorted =. data._.orderBy(data, ['city', 'price'], ['asc','desc'])

You can use lodash orderBy function lodash

It takes two params array of fields, and array of directions ('asc','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"}
    ];

var sorted =. data._.orderBy(data, ['city', 'price'], ['asc','desc'])
与酒说心事 2025-02-20 20:25:25

在这里,您可以尝试较小且方便的方法来按多个字段进行排序!

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((a, b)=> {
  if (a.city === b.city){
    return a.price < b.price ? -1 : 1
  } else {
    return a.city < b.city ? -1 : 1
  }
})

console.log(homes);

Here, you can try the smaller and convenient way to sort by multiple fields!

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((a, b)=> {
  if (a.city === b.city){
    return a.price < b.price ? -1 : 1
  } else {
    return a.city < b.city ? -1 : 1
  }
})

console.log(homes);

伪装你 2025-02-20 20:25:25

最快,最简单的方法是使用或链接,就像这里已经建议的许多人一样。对于指定的示例数据,它看起来像这样:

homes.sort((a, b) =>
    a.city.localeCompare(b.city)
    || (Number(b.price) - Number(a.price))
);

但是,如果您想要可配置的东西(以及在打字稿中),则可以尝试以下代码:

代码(typescript)

export type Comparer<T> = (a: T, b: T) => number;

export type CompareCriterion<TItem, TValue> = {
    selector: (item: TItem) => TValue,
    descending?: boolean,
    comparer?: Comparer<TValue>,
};

export const defaultComparer = <T>(a: T, b: T): number => {
    return a === b ? 0 : a > b ? 1 : -1;
};

export const defaultNumberComparer = (a: number, b: number): number => {
    return a - b;
};

export const StringComparer = (() => {
    const currentLocale = new Intl.Collator(navigator.language, { usage: 'sort', sensitivity: 'variant', caseFirst: 'upper' });
    const currentLocaleIgnoreCase = new Intl.Collator(navigator.language, { usage: 'sort', sensitivity: 'accent', caseFirst: 'upper' });
    const invariantLocale = new Intl.Collator('en', { usage: 'sort', sensitivity: 'variant', caseFirst: 'upper' });
    const invariantLocaleIgnoreCase = new Intl.Collator('en', { usage: 'sort', sensitivity: 'accent', caseFirst: 'upper' });
    return {
        // eslint-disable-next-line @typescript-eslint/unbound-method
        currentLocale: currentLocale.compare,

        // eslint-disable-next-line @typescript-eslint/unbound-method
        currentLocaleIgnoreCase: currentLocaleIgnoreCase.compare,

        // eslint-disable-next-line @typescript-eslint/unbound-method
        invariantLocale: invariantLocale.compare,

        // eslint-disable-next-line @typescript-eslint/unbound-method
        invariantLocaleIgnoreCase: invariantLocaleIgnoreCase.compare,
    };
})();

export const defaultStringComparer = (a: string, b: string): number => {
    return a.localeCompare(b);
};

export const defaultDateComparer = (a: Date, b: Date): number => {
    return a.getTime() - b.getTime();
};

export class ComparerBuilder<TItem> {
    #criteria: ((next?: Comparer<TItem>) => Comparer<TItem>)[] = [];

    add<TValue>(criterion: CompareCriterion<TItem, TValue>): ComparerBuilder<TItem> {
        this.#criteria.push(next => ComparerBuilder.#createComparer(criterion, next));
        return this;
    }

    static #createComparer<TItem, TValue>(
        criterion: CompareCriterion<TItem, TValue>,
        next?: Comparer<TItem>,
    ): Comparer<TItem> {
        const comparer = criterion.comparer ?? defaultComparer;
        return (a: TItem, b: TItem) => {
            const av = criterion.selector(a);
            const bv = criterion.selector(b);
            const comparison = comparer(av, bv);
            if (comparison === 0)
                return next?.(a, b) ?? 0;
            return criterion.descending ? -comparison : comparison;
        };
    }

    build(bottomComparer?: Comparer<TItem>): Comparer<TItem> {
        let comparer = bottomComparer;
        for (let i = this.#criteria.length - 1; i >= 0; i--)
            comparer = this.#criteria[i](comparer);
        return comparer ?? defaultComparer;
    }
}

用法示例

// Declare item type.
type Item = { key: number, code: string, name: string, price: number };

// Build comparer from provided criteria.
const comparer = new ComparerBuilder<Item>()
    .add({ selector: v => v.price })
    .add({ selector: v => v.code, descending: true, comparer: StringComparer.currentLocaleIgnoreCase })
    .add({ selector: v => v.name, comparer: new Intl.Collator('ru').compare })
    .add({ selector: v => v.key, comparer: defaultNumberComparer })
    .build();

// Use built comparer for multiple calls.
const items1: Item[] = [{ key: 1, code: 'FOO', name: 'bar', price: 100.98 }, { key: 2, code: 'FOa', name: 'baz', price: 100.98 }];
// Note: we are using spread operator to prevent original array mutation (sort method works so).
const sortedItems1 = [...items1].sort(comparer);

const items2: Item[] = [{ key: 1, code: 'BAR', name: 'foo', price: 100.98 }];
// Note: we are using spread operator to prevent original array mutation (sort method works so).
const sortedItems2 = [...items2].sort(comparer);

Fastest and easiest way is to use OR-chaining as many of people already suggested here. For the specified example data it looks like this:

homes.sort((a, b) =>
    a.city.localeCompare(b.city)
    || (Number(b.price) - Number(a.price))
);

But if you want something configurable (and in TypeScript), you can try the following code:

Code (TypeScript)

export type Comparer<T> = (a: T, b: T) => number;

export type CompareCriterion<TItem, TValue> = {
    selector: (item: TItem) => TValue,
    descending?: boolean,
    comparer?: Comparer<TValue>,
};

export const defaultComparer = <T>(a: T, b: T): number => {
    return a === b ? 0 : a > b ? 1 : -1;
};

export const defaultNumberComparer = (a: number, b: number): number => {
    return a - b;
};

export const StringComparer = (() => {
    const currentLocale = new Intl.Collator(navigator.language, { usage: 'sort', sensitivity: 'variant', caseFirst: 'upper' });
    const currentLocaleIgnoreCase = new Intl.Collator(navigator.language, { usage: 'sort', sensitivity: 'accent', caseFirst: 'upper' });
    const invariantLocale = new Intl.Collator('en', { usage: 'sort', sensitivity: 'variant', caseFirst: 'upper' });
    const invariantLocaleIgnoreCase = new Intl.Collator('en', { usage: 'sort', sensitivity: 'accent', caseFirst: 'upper' });
    return {
        // eslint-disable-next-line @typescript-eslint/unbound-method
        currentLocale: currentLocale.compare,

        // eslint-disable-next-line @typescript-eslint/unbound-method
        currentLocaleIgnoreCase: currentLocaleIgnoreCase.compare,

        // eslint-disable-next-line @typescript-eslint/unbound-method
        invariantLocale: invariantLocale.compare,

        // eslint-disable-next-line @typescript-eslint/unbound-method
        invariantLocaleIgnoreCase: invariantLocaleIgnoreCase.compare,
    };
})();

export const defaultStringComparer = (a: string, b: string): number => {
    return a.localeCompare(b);
};

export const defaultDateComparer = (a: Date, b: Date): number => {
    return a.getTime() - b.getTime();
};

export class ComparerBuilder<TItem> {
    #criteria: ((next?: Comparer<TItem>) => Comparer<TItem>)[] = [];

    add<TValue>(criterion: CompareCriterion<TItem, TValue>): ComparerBuilder<TItem> {
        this.#criteria.push(next => ComparerBuilder.#createComparer(criterion, next));
        return this;
    }

    static #createComparer<TItem, TValue>(
        criterion: CompareCriterion<TItem, TValue>,
        next?: Comparer<TItem>,
    ): Comparer<TItem> {
        const comparer = criterion.comparer ?? defaultComparer;
        return (a: TItem, b: TItem) => {
            const av = criterion.selector(a);
            const bv = criterion.selector(b);
            const comparison = comparer(av, bv);
            if (comparison === 0)
                return next?.(a, b) ?? 0;
            return criterion.descending ? -comparison : comparison;
        };
    }

    build(bottomComparer?: Comparer<TItem>): Comparer<TItem> {
        let comparer = bottomComparer;
        for (let i = this.#criteria.length - 1; i >= 0; i--)
            comparer = this.#criteria[i](comparer);
        return comparer ?? defaultComparer;
    }
}

Usage example

// Declare item type.
type Item = { key: number, code: string, name: string, price: number };

// Build comparer from provided criteria.
const comparer = new ComparerBuilder<Item>()
    .add({ selector: v => v.price })
    .add({ selector: v => v.code, descending: true, comparer: StringComparer.currentLocaleIgnoreCase })
    .add({ selector: v => v.name, comparer: new Intl.Collator('ru').compare })
    .add({ selector: v => v.key, comparer: defaultNumberComparer })
    .build();

// Use built comparer for multiple calls.
const items1: Item[] = [{ key: 1, code: 'FOO', name: 'bar', price: 100.98 }, { key: 2, code: 'FOa', name: 'baz', price: 100.98 }];
// Note: we are using spread operator to prevent original array mutation (sort method works so).
const sortedItems1 = [...items1].sort(comparer);

const items2: Item[] = [{ key: 1, code: 'BAR', name: 'foo', price: 100.98 }];
// Note: we are using spread operator to prevent original array mutation (sort method works so).
const sortedItems2 = [...items2].sort(comparer);
终遇你 2025-02-20 20:25:25

容易且易于理解:

var homes = [
   { 'city': 'Dallas', 'state': 'TX', 'zip': '75201', 'price': '162500'},
   { 'city`enter code here`': 'Bevery Hills', 'state': 'CA', 'zip': '90210', 'price': '319250'},
   { 'city': 'Dallas', 'state': 'TX', 'zip': '75000', 'price': '556699'},
   { 'city': 'New York', 'state': 'NY', 'zip': '00010', 'price': '962500'}
];

homes.sort(compareMultiple(['zip', '-state', 'price']));

function compareMultiple (criteria) {
   return function (a, b) {
      for (let key of criteria) {
         var order = key.includes('-') ? -1 : 1;
         if (!a[key]) return -order;
         if (!b[key]) return order;
         if (!a[key] && ![key]) return 0;
         if (a[key] > b[key]) return order;
         if (a[key] < b[key]) return -order;
      }
      return 0;
   };
}

easy and understandable:

var homes = [
   { 'city': 'Dallas', 'state': 'TX', 'zip': '75201', 'price': '162500'},
   { 'city`enter code here`': 'Bevery Hills', 'state': 'CA', 'zip': '90210', 'price': '319250'},
   { 'city': 'Dallas', 'state': 'TX', 'zip': '75000', 'price': '556699'},
   { 'city': 'New York', 'state': 'NY', 'zip': '00010', 'price': '962500'}
];

homes.sort(compareMultiple(['zip', '-state', 'price']));

function compareMultiple (criteria) {
   return function (a, b) {
      for (let key of criteria) {
         var order = key.includes('-') ? -1 : 1;
         if (!a[key]) return -order;
         if (!b[key]) return order;
         if (!a[key] && ![key]) return 0;
         if (a[key] > b[key]) return order;
         if (a[key] < b[key]) return -order;
      }
      return 0;
   };
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文