从这个最初的问题,我将如何在多个字段应用排序?
使用这种稍作调整的结构,我将如何排序城市(上升)和价格(下降)?
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"}
];
我喜欢的事实是,给出的答案提供了一个一般的方法。在我计划使用这段代码的地方,我将不得不对日期以及其他东西进行排序。“启动”对象的能力似乎很方便,如果不是有点麻烦的话。
我试图把这个答案构建成一个很好的通用示例,但我运气不太好。
以下是@ 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 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相等,它将尝试下一个字段,直到没有可用字段。
一种多维排序方法,基于这个答案:
更新:这是一个“优化”版本。它做了更多的预处理,并预先为每个排序选项创建了一个比较函数。它可能需要更多的内存(因为它为每个排序选项存储了一个函数,但它应该更好一点,因为它不必在比较期间确定正确的设置。不过我没有做过任何侧写。
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