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

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

使用这个稍作调整的结构,我将如何排序城市(升序)&那么价格(下降)呢?

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

我喜欢的事实是,回答提供了一个一般的方法。在我计划使用这段代码的地方,我将不得不对日期以及其他东西进行排序。“启动”对象的能力似乎很方便,如果不是有点麻烦的话。

我已经尝试将这个回答构建成一个很好的通用示例,但我没有太多的运气。

356518 次浏览

多维排序方法,基于这个答案:

更新:这是一个“优化”版本。它做了更多的预处理,并预先为每个排序选项创建了一个比较函数。它可能需要更多的内存(因为它为每个排序选项存储了一个函数,但它应该更好一点,因为它不必在比较期间确定正确的设置。不过我没有做过任何侧写。

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

DEMO

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

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

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

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

默认情况下,该函数将按升序对每个属性/键排序。如果你想让一个特定的键按降序排序,那么可以传入一个这样格式的数组:['property_name', true]

下面是该函数的一些示例用法,后面附有解释(其中homes是一个包含对象的数组):

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

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

objSort(homes, 'city', true)—>按城市然后价格排序(升序,case 敏感的)

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

今天我做了一个相当通用的多特征排序器。你可以看看这个by .js: https://github.com/Teun/thenBy.js

它允许您使用标准数组。排序,但使用firstBy(). thenby (). thenby()样式。它比上面发布的解决方案少了很多代码和复杂性。

对于你的具体问题,一个非通用的,简单的解决方案:

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

以下是@ 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"}
];


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

这里' affiliation duedate '和'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;
})

下面是我基于施瓦兹变换成语的解决方案,希望你觉得有用。

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

对两个日期字段和一个数字字段的排序示例:

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/

简单的一个:

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

我喜欢snowburn的方法,但它需要调整来测试城市的等效性,而不是差异。

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

这是一个完全的欺骗,但我认为它为这个问题增加了价值,因为它基本上是一个罐装的库函数,你可以开箱即用。

如果你的代码可以访问lodash或像underscore这样的lodash兼容库,那么你可以使用_.sortBy方法。下面的代码段直接从lodash文档. xml文件中复制。

示例中的注释结果看起来像是返回数组的数组,但这只是显示了顺序,而不是实际的结果,它是一个对象数组。

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

另一种方式

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

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

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

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

改编自@chriskelly的回答。


大多数答案都忽略了,如果价值在1万美元以下或超过100万美元,价格将无法正确排序。原因是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>


a fiddle to test with

哇,这里有一些复杂的解。如此复杂,我决定想出一些更简单但也相当强大的东西。在这里;

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

这将首先根据属性的优先级排序,然后根据属性的值进行排序。

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

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排序数组的多个(数字)字段

下面是一种可扩展的按多个字段排序的方法。

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)对于字符串是普遍的支持,如果a<ba==ba>b则返回-1,0,1。
  • 减法适用于数值字段,因为如果a<ba==ba>b,则a - b给出-,0,+。
  • 最后一行中的||使city优先于price
  • 否定用于反转任何字段的顺序,如-price_order
  • 在or-chain中添加新字段:return city_order || -price_order || date_order;
  • <强> < / >强比较日期带有减法,因为日期计算从1970年开始转换为毫秒
  • 布尔与减法比较,这是保证将true和false转换为1和0(因此减法产生-1或0或1)。
    var goodness_order = Boolean(left.is_good) - Boolean(right.is_good)
    这是非常不寻常的,我建议注意布尔构造函数,即使它们已经是布尔构造函数。

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

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

以下是我的简历,请参考,并举例说明:

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

一个动态的方法来做到这与多个键:

  • 从排序的每个col/key中过滤唯一的值
  • 按顺序排列或颠倒
  • 根据indexOf(value)键值为每个对象添加weights width zeropad
  • 使用计算的权重进行排序

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

使用:

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

我一直在寻找类似的东西,最后得到了这个:

首先,我们有一个或多个排序函数,总是返回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时,将调用下一个排序函数进行进一步排序。

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

用Typescript编写。对于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,
}
]));

只是另一种选择。考虑使用以下效用函数:

/** 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。宽度”等。

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

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相等,它将尝试下一个字段,直到没有可用字段。

按多个字段排序对象数组的最简单方法:

 let 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) => (a.city > b.city) ? 1 : -1);
< p >输出: “Bevery Hills" “Dallas" “Dallas" “Dallas" “新York" < / p >
// 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);

只需遵循排序标准列表

即使要封装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))

可以使用lodash或derby函数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'])

通过添加两个辅助函数,可以简单地解决这类问题。sortByKey接受一个数组和一个函数,该函数应返回一个项目列表,与每个数组项进行比较。

这利用了javascript使用[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}]'

我从Python的list.sort函数中偷取了这个想法。

< p >为什么复杂化?只需要整理两次!这是完美的: (只是确保将重要性顺序从最低到最高颠倒):

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

通过添加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创建比较器函数。为比较器参数ab调用所提供的函数fn。然后比较两个函数调用的结果。如果resultA < resultB返回-1,如果resultA > resultB返回1,否则返回0。这些返回值与升序方向相对应。

    也可以写成这样:

    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是超级简单的,因为它只调用asc,但是交换了ab参数,结果是降序而不是升序。

  • fallback(可能有一个更好的名字)允许我们对一个sort使用多个比较器函数。

    ascdesc都可以自己传递给sort

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

    然而,如果你想组合多个比较器函数,就会有一个问题。sort只接受一个比较器函数。fallback将多个比较器函数组合成一个比较器。

    第一个比较器使用参数ab调用,如果比较器返回值0(意味着两个值相等),则返回下一个比较器。这将一直持续,直到找到一个非-0值,或者直到调用所有比较器,在这种情况下,返回值为0

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

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

需要注意的一点是,可以为undefined的值在与<>比较时总是返回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);
};
}

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

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

为了简化操作,可以使用这些辅助函数。

您可以根据需要对任意多个字段进行排序。对于每个排序字段,指定属性名,然后可选地指定-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)