如何按多个字段对对象数组排序?


147

从这个原始问题开始,我将如何对多个字段进行排序?

使用这种略微调整的结构,我如何对城市(升序)和价格(降序)进行排序?

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

我喜欢这个事实,而不是给出一个提供通用方法的答案。在计划使用此代码的地方,我将必须对日期以及其他内容进行排序。“灌注”物体的功能似乎很方便,即使有点麻烦。

我试图将这个答案构建成一个很好的通用示例,但是我运气不高。


您要搜索还是排序?
菲利克斯·克林

您使用所链接的第二个答案到底遇到了什么问题?
佳能

它不够通用。我似乎添加代码海的时候我只是想说sort(["first-field", "ASC"], ["second-field", "DSC"]); 这是进一步复杂化,当我尝试的第一个答案的“引”逻辑添加,这样我可以处理日期,不区分大小写等
麦克

或者,您可以对每个字段施加权重
onmyway133 '16

你可以检查 lodash.com/docs/4.17.11#orderBy,如果您确定使用lodash
Deepanshu阿罗拉

Answers:


83

基于此答案的多维排序方法:

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

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

演示


原始功能:

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

演示


2
作为记录,可以通过预处理参数列表并创建统一的“排序选项数组”来改进此功能。这留给读者练习;)
Felix Kling

@Mike:好的...最终;)您会看到,由于选项已经过预处理,因此它现在变得更加复杂,但是最终的比较功能(请参见注释)要简单得多,(希望)这会带来更好的性能。您拥有的排序选项越多,使用此方法的优势就越大。
Felix Kling

165

为您的确切问题提供非通用,简单的解决方案:

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

6
我认为此演示是OP想要的=> jsfiddle.net/zJ6UA/533
Amin Jafari

3
这是正确的想法,但逻辑是错误的。您不能从另一个字符串中减去非数字字符串,该if语句没有任何意义。
JLRishe

6
您可以a.localeCompare(b)在最后一行使用字符串进行比较... 请参阅文档
Michael P

2
第一次城市比较不是要检查是否平等,而不是不平等?换句话说,这行不应该if (a.city === b.city)吗?也就是说,如果两个城市相同,则比较价格,否则比较城市。
史蒂文·兰德斯'18

2
最好的答案之一。tnx。
jonathana

54

您可以使用链式排序方法,方法是取值的增量,直到达到不等于零的值。

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

16
我想念什么吗?为什么要使用60行代码完成可以在1中完成的工作。简单,清晰,简洁。应该是IMO接受的答案。
Erez Cohen

SO现在的主要问题之一是旧答案-经常被使用新语言功能(例如ES5-6-7)的更好解决方案所取代,并保持了旧分数,我们所有人都必须向下滚动才能找到“真正的”最佳答案解决方案!SO应该随着时间的推移而过期,以解决此问题,因为随着时间的流逝,问题变得越来越严重。
安迪·洛伦兹

53

这是一种简单的功能方法。使用数组指定排序顺序。加上减号以指定降序。

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


6
我发现此函数非常简洁,因此根据解析器的不同,我的性能提高了90%。我做了一个要点测试套件
php_nub_qq

基于它看起来像数字样本数据进行排序预期,但是当我试图在那里排序更像字符串实现这个号码...... [10,100,11,9]。我错过了什么?
马克·卡彭特(Marc Carpenter Jr)于2008年

@MarkCarpenterJr。不明白你的意思。我的示例对数字类型进行了正确排序。您能否将实现作为一个问题分享给我,并在评论中引用我,以便我看到呢?然后我可以检查。
chriskelly,2017年

@MarkCarpenterJr。刚发现它。我在评论中添加了解释。
chriskelly,2017年

32

今天,我做了一个非常通用的多功能分类器。您可以在此处查看thenBy.js:https//github.com/Teun/thenBy.js

它允许您使用标准的Array.sort,但具有firstBy()。thenBy()。thenBy()样式。它比上面发布的解决方案少了代码和复杂性。


8
好吧,当您拨打3次电话时,对于第二次通话没有影响的项目,不能保证第二次通话不会使第一个通话的顺序保持不变。
Teun D

13

以下函数将允许您对一个或多个属性(每个属性的升序(默认)或降序)的对象数组进行排序,并允许您选择是否执行区分大小写的比较。默认情况下,此函数执行不区分大小写的排序。

第一个参数必须是包含对象的数组。后续参数必须是逗号分隔的字符串列表,这些字符串引用要排序的不同对象属性。最后一个参数(可选)是一个布尔值,用于选择是否执行区分大小写的排序- true用于区分大小写的排序。

默认情况下,该函数将按升序对每个属性/键进行排序。如果您希望特定的键以降序排序,请改为以以下格式传递数组:['property_name', true]

以下是该函数的一些示例用法,并附有说明(其中 homes是包含对象的数组):

objSort(homes, 'city') ->按城市排序(升序,不区分大小写)

objSort(homes, ['city', true]) ->按城市排序(降序,不区分大小写)

objSort(homes, 'city', true)->按城市排序,然后按价格排序(升序,区分大小写

objSort(homes, 'city', 'price') ->按城市排序,然后按价格排序(都升序,不区分大小写)

objSort(homes, 'city', ['price', true]) ->按城市排序(升序),然后按价格(降序)排序,不区分大小写)

事不宜迟,这里是函数:

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

8

这是一个完整的秘籍,但我认为它为这个问题增加了价值,因为它基本上是一个固定的库函数,您可以直接使用它。

如果您的代码可以访问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]]

7

这是另一个可能更接近于您对语法的想法

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

演示:http//jsfiddle.net/Nq4dk/2/


编辑:只是为了好玩,这里有一个变体,只需要一个类似sql的字符串,因此您可以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);
    });
}

由于数组比较,此解决方案很干净,但性能不佳。您只需浏览一下属性即可跟踪比较值,如果比较值不为零,则返回。那快很多。
amankapur91 2014年

4

比较简单的一个:

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

3

我喜欢SnowBurnt的方法,但需要进行调整以测试在城市中的等效性,没有区别。

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

3

这是一个通用的多维排序,允许在每个级别上进行反转和/或映射。

用打字稿写的。对于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']));

按语言代码的名称而不是语言代码(请参阅map)对它们进行排序,然后按降序版本(请参见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,
  }
]));

2

使用MULTIPLE键执行此操作的动态方式:

  • 从排序的每个列/键中过滤唯一值
  • 整理或反转
  • 根据indexOf(value)键值为每个对象添加权重宽度零填充
  • 使用重量加权排序

在此处输入图片说明

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

用:

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

在此处输入图片说明


1

这是@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条以上的记录,则可能应该使用服务器端排序和过滤。

这段代码没有处理区分大小写的问题,但是我让读者来处理这个琐碎的修改。


1

这是我基于Schwartzian转换惯用语的解决方案,希望您觉得有用。

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

1
我在此页面(以及其他页面)上尝试了一些方法。a8m的这种解决方案仅适合我的情况:gist.github.com/cemerson/f1f1434286c1262b403f3d85c96688e0
Christopher D. Emerson

1

其他方式

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


0
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数组进行排序。完全不需要更改功能主体


0

@chriskelly的答案的改编。


大多数答案都忽略了,如果价格在1万甚至更低或超过一百万,则价格将无法正确排序。原因是JS按字母顺序排序。在这里,答案很好,为什么JavaScript无法对“ 5,10,1” 进行排序以及在此处如何正确对整数数组进行排序

最终,如果我们要排序的字段或节点是一个数字,我们必须进行一些评估。我并不是说parseInt()在这种情况下使用是正确的答案,排序后的结果更为重要。

var homes = [{
  "h_id": "2",
  "city": "Dallas",
  "state": "TX",
  "zip": "75201",
  "price": "62500"
}, {
  "h_id": "1",
  "city": "Dallas",
  "state": "TX",
  "zip": "75201",
  "price": "62510"
}, {
  "h_id": "3",
  "city": "Dallas",
  "state": "TX",
  "zip": "75201",
  "price": "162500"
}, {
  "h_id": "4",
  "city": "Bevery Hills",
  "state": "CA",
  "zip": "90210",
  "price": "319250"
}, {
  "h_id": "6",
  "city": "Dallas",
  "state": "TX",
  "zip": "75000",
  "price": "556699"
}, {
  "h_id": "5",
  "city": "New York",
  "state": "NY",
  "zip": "00010",
  "price": "962500"
}];

homes.sort(fieldSorter(['price']));
// homes.sort(fieldSorter(['zip', '-state', 'price'])); // alternative

function fieldSorter(fields) {
  return function(a, b) {
    return fields
      .map(function(o) {
        var dir = 1;
        if (o[0] === '-') {
          dir = -1;
          o = o.substring(1);
        }
        if (!parseInt(a[o]) && !parseInt(b[o])) {
          if (a[o] > b[o]) return dir;
          if (a[o] < b[o]) return -(dir);
          return 0;
        } else {
          return dir > 0 ? a[o] - b[o] : b[o] - a[o];
        }
      })
      .reduce(function firstNonZeroValue(p, n) {
        return p ? p : n;
      }, 0);
  };
}
document.getElementById("output").innerHTML = '<pre>' + JSON.stringify(homes, null, '\t') + '</pre>';
<div id="output">

</div>


测试的小提琴


问题出在您要排序的数据上。price在示例中为字符串格式。如果您希望它与我的示例一起正常使用,请使用map来转换要数字格式的字段。即const correctedHomes = homes.map(h => ({...h, price: +h.price}))
chriskelly

0

哇,这里有一些复杂的解决方案。如此复杂,我决定想出一些更简单但也很强大的工具。这里是;

function sortByPriority(data, priorities) {
  if (priorities.length == 0) {
    return data;
  }

  const nextPriority = priorities[0];
  const remainingPriorities = priorities.slice(1);

  const matched = data.filter(item => item.hasOwnProperty(nextPriority));
  const remainingData = data.filter(item => !item.hasOwnProperty(nextPriority));

  return sortByPriority(matched, remainingPriorities)
    .sort((a, b) => (a[nextPriority] > b[nextPriority]) ? 1 : -1)
    .concat(sortByPriority(remainingData, remainingPriorities));
}

这是一个如何使用它的示例。

const data = [
  { id: 1,                         mediumPriority: 'bbb', lowestPriority: 'ggg' },
  { id: 2, highestPriority: 'bbb', mediumPriority: 'ccc', lowestPriority: 'ggg' },
  { id: 3,                         mediumPriority: 'aaa', lowestPriority: 'ggg' },
];

const priorities = [
  'highestPriority',
  'mediumPriority',
  'lowestPriority'
];


const sorted = sortByPriority(data, priorities);

这将首先按属性的优先级排序,然后按属性的值排序。


0

这是按多个字段排序的可扩展方法。

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<ba==ba>b分别。
  • 减法适用于数字字段。
  • ||最后一行的city优先级高于price
  • 在任何字段中取反顺序,如 -price_order
  • 日期比较var date_order = new Date(left.date) - new Date(right.date);就像NUMERICS因为日期的功能自1970年变成毫秒。
  • 将字段添加到或链中, return city_order || -price_order || date_order;

0

我认为这可能是最简单的方法。

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

这真的很简单,我尝试了3种不同的键值对,效果很好。

这是一个简单的示例,请查看链接以获取更多详细信息

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

以下是我的供您参考的示例:

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、1或-1:

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

您可以为要排序的每个其他属性创建更多函数。

然后,我有一个将这些排序功能组合为一个的功能:

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

这可以用于以可读的方式组合上述排序功能:

const sorter = createSorter(sortByTitle, sortByYear)

items.sort(sorter)

当排序函数返回0时,将调用下一个排序函数以进行进一步排序。


0

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

/** 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.width”等),该函数甚至可以进一步推广。


0

这是一种递归算法,用于按多个字段排序,同时有机会在比较之前格式化值。

var data = [
{
    "id": 1,
    "ship": null,
    "product": "Orange",
    "quantity": 7,
    "price": 92.08,
    "discount": 0
},
{
    "id": 2,
    "ship": "2017-06-14T23:00:00.000Z".toDate(),
    "product": "Apple",
    "quantity": 22,
    "price": 184.16,
    "discount": 0
},
...
]
var sorts = ["product", "quantity", "ship"]

// comp_val formats values and protects against comparing nulls/undefines
// type() just returns the variable constructor
// String.lower just converts the string to lowercase.
// String.toDate custom fn to convert strings to Date
function comp_val(value){
    if (value==null || value==undefined) return null
    var cls = type(value)
    switch (cls){
        case String:
            return value.lower()
    }
    return value
}

function compare(a, b, i){
    i = i || 0
    var prop = sorts[i]
    var va = comp_val(a[prop])
    var vb = comp_val(b[prop])

    // handle what to do when both or any values are null
    if (va == null || vb == null) return true

    if ((i < sorts.length-1) && (va == vb)) {
        return compare(a, b, i+1)
    } 
    return va > vb
}

var d = data.sort(compare);
console.log(d);

如果a和b相等,则将尝试下一个字段,直到没有可用的字段为止。


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

为什么不将所有内容放到一个函数中呢?如果城市不相等,则返回它们的差异,否则,差异价格。
疯狂物理学家,2014年

-1

这里的'AffiliateDueDate'和'Title'是列,都以升序排列。

array.sort(function(a, b) {

               if (a.AffiliateDueDate > b.AffiliateDueDate ) return 1;
               else if (a.AffiliateDueDate < b.AffiliateDueDate ) return -1;
               else if (a.Title > b.Title ) return 1;
               else if (a.Title < b.Title ) return -1;
               else return 0;
             })

-1

在两个日期字段和一个数字字段示例上进行排序:

var generic_date =  new Date(2070, 1, 1);
checkDate = function(date) {
  return Date.parse(date) ? new Date(date): generic_date;
}

function sortData() {  
  data.sort(function(a,b){
    var deltaEnd = checkDate(b.end) - checkDate(a.end);
    if(deltaEnd) return deltaEnd;

    var deltaRank = a.rank - b.rank;
    if (deltaRank) return deltaRank;

    var deltaStart = checkDate(b.start) - checkDate(a.start);
    if(deltaStart) return deltaStart;

    return 0;
  });
}

http://jsfiddle.net/hcWgf/57/


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


-1

这个简单的解决方案如何:

const sortCompareByCityPrice = (a, b) => {
    let comparison = 0
    // sort by first criteria
    if (a.city > b.city) {
        comparison = 1
    }
    else if (a.city < b.city) {
        comparison = -1
    }
    // If still 0 then sort by second criteria descending
    if (comparison === 0) {
        if (parseInt(a.price) > parseInt(b.price)) {
            comparison = -1
        }
        else if (parseInt(a.price) < parseInt(b.price)) {
            comparison = 1
        }
    }
    return comparison 
}

基于此问题,javascript按多个(数字)字段对数组进行排序

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.