从这个最初的问题,我将如何在多个字段应用排序?

使用这种稍作调整的结构,我将如何排序城市(上升)和价格(下降)?

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 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数组进行排序。根本不需要改变函数体

其他回答

这里有一个简单的泛型函数方法。使用数组指定排序顺序。前置减号以指定降序。

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

只需遵循排序标准列表

即使要封装36个排序标准,这段代码也将始终保持可读和可理解

Nina在这里提出的解决方案当然非常优雅,但它意味着要知道在布尔逻辑中,值为0对应的值为false,并且布尔测试在JavaScript中可以返回除true / false以外的值(这里是数值),这对于初学者来说总是令人困惑。

还要考虑谁需要维护您的代码。也许会是你:想象一下你自己花了几天的时间在另一个人的代码上,然后有了一个有害的错误……你读了几千行充满技巧的文章,都累坏了

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

通过添加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);

如果你向下滚动代码片段,你可能已经看到了helper:

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

也可以为fallback()提供自定义比较器函数。假设您想使用localeCompare()而不是比较字符串与<和>。在这种情况下,您可以将asc(home => home.city)替换为(a, b) => a.city.localeCompare(b.city)。

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

需要注意的一点是,在与<和>比较时,未定义的值总是返回false。因此,如果一个值可能缺失,您可能希望首先根据它的存在进行排序。

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

以下是@ snowburn的解决方案的通用版本:

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


})

这是基于我正在使用的排序例程。我没有测试这个特定的代码,所以它可能有错误,但你知道的。其思想是基于第一个表示差异的字段进行排序,然后停止并转到下一个记录。因此,如果您按三个字段排序,并且compare中的第一个字段足以确定正在排序的两个记录的排序顺序,那么将返回该排序结果并转到下一个记录。

我在5000条记录上测试了它(实际上使用了更复杂的排序逻辑),它在眨眼之间就做到了。如果实际向客户端加载超过1000条记录,则可能应该使用服务器端排序和过滤。

这段代码不区分大小写,我把它留给读者来处理这个微不足道的修改。

另一种方式

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