我迫切需要实现客户端排序,它通过我们的tastypie api模拟排序,它可以占用多个字段并返回已排序的数据。因此,如果我有以下数据:
arr = [
{ name: 'Foo LLC', budget: 3500, number_of_reqs: 1040 },
{ name: '22nd Amendment', budget: 1500, number_of_reqs: 2000 },
{ name: 'STS 10', budget: 50000, number_of_reqs: 500 },
...
etc.
]
并给出要排序的列,例如:['name', '-number_of_reqs']
}它应按name
(升序)和number_of_reqs
(降序)排序。我无法理解这一点,
首先,它必须是#34;自然排序"如果我们谈论排序单个列,它应该很容易获得,但我需要能够排序多个。
此外,我不确定为什么在使用lodash _.sortBy
时,我会得到不同的结果(来自api如何做到这一点)? _.sortBy
不是"自然"或者我们的api坏了吗?
此外,我一直在寻找一个优雅的解决方案。刚刚开始使用Ramdajs,它非常棒。我敢打赌,建立我需要使用的排序会更容易吗?我已经尝试过了,仍然无法做到正确。帮助不大?
upd:
我找到this并将其与Ramda一起使用,如下所示:
fn = R.compose(R.sort(naturalSort), R.pluck("name"))
fn(arr)
似乎适用于平面数组,但我仍然需要找到一种方法将它应用于我的数组中的多个字段
答案 0 :(得分:8)
fn = R.compose(R.sort(naturalSort), R.pluck("name"))
似乎正在运作
真的?我希望返回一个已排序的名称数组,而不是按名称属性对对象数组进行排序。
遗憾的是,使用sortBy
并不能让我们提供自定义比较功能(自然排序所需),并且在单个值中组合多个列可能是一致的,但是很麻烦。
我仍然不知道如何为多个字段执行此操作
功能编程在这里可以做很多事,不幸的是Ramda并没有为比较器配备有用的功能(R.comparator
除外)。我们需要三个额外的助手:
on
(与one from Haskell一样),它采用a -> b
转换和b -> b -> Number
比较器函数来生成两个a
上的比较器秒。我们可以像这样用Ramda创建它:
var on = R.curry(function(map, cmp) { return R.useWith(cmp, map, map); return R.useWith(cmp, [map, map]); // since Ramda >0.18 });
or
- 就像||
一样,但数字不仅限于R.or
等布尔值。这可用于将两个比较器链接在一起,如果第一个产生0
(相等),则仅调用第二个比较器。或者,可以使用像thenBy
这样的库。但是让我们自己来定义它:
var or = R.curry(function(fst, snd, a, b) { return fst(a, b) || snd(a, b); });
negate
- 反转比较的函数:
function negate(cmp) { return R.compose(R.multiply(-1), cmp); }
现在,配备这些我们只需要我们的比较功能(自然排序是您找到的自适应版本,另请参阅Sort Array Elements (string with numbers), natural sort了解更多信息):
var NUMBER_GROUPS = /(-?\d*\.?\d+)/g;
function naturalCompare(a, b) {
var aa = String(a).split(NUMBER_GROUPS),
bb = String(b).split(NUMBER_GROUPS),
min = Math.min(aa.length, bb.length);
for (var i = 0; i < min; i++) {
var x = aa[i].toLowerCase(),
y = bb[i].toLowerCase();
if (x < y) return -1;
if (x > y) return 1;
i++;
if (i >= min) break;
var z = parseFloat(aa[i]) - parseFloat(bb[i]);
if (z != 0) return z;
}
return aa.length - bb.length;
}
function stringCompare(a, b) {
a = String(a); b = String(b);
return +(a>b)||-(a<b);
}
function numberCompare(a, b) {
return a-b;
}
现在我们可以准确地构建您想要的对象的比较:
fn = R.sort(or(on(R.prop("name"), naturalCompare),
on(R.prop("number_of_reqs"), negate(numberCompare))));
fn(arr)
答案 1 :(得分:1)
我认为这很有效。
var arr = [
{ name: 'Foo LLC', budget: 3500, number_of_reqs: 1040 },
{ name: '22nd Amendment', budget: 1500, number_of_reqs: 2000 },
{ name: 'STS 10', budget: 50000, number_of_reqs: 5000 },
{ name: 'STS 10', budget: 50000, number_of_reqs: 500 }
];
var columns = ['name', 'number_of_reqs'];
var NUMBER_GROUPS = /(-?\d*\.?\d+)/g;
var naturalSort = function (a, b, columnname) {
var a_field1 = a[columnname],
b_field1 = b[columnname],
aa = String(a_field1).split(NUMBER_GROUPS),
bb = String(b_field1).split(NUMBER_GROUPS),
min = Math.min(aa.length, bb.length);
for (var i = 0; i < min; i++) {
var x = parseFloat(aa[i]) || aa[i].toLowerCase(),
y = parseFloat(bb[i]) || bb[i].toLowerCase();
if (x < y) return -1;
else if (x > y) return 1;
}
return 0;
};
arr.sort(function(a, b) {
var result;
for (var i = 0; i < columns.length; i++) {
result = naturalSort(a, b, columns[i]);
if (result !== 0) return result; // or -result for decending
}
return 0; //If both are exactly the same
});
console.log(arr);
答案 2 :(得分:1)
Bergi的答案很有用,也很有趣,但它改变了你要求的API。这是创建您正在寻找的API的一个:
var multisort = (function() {
var propLt = R.curry(function(name, a, b) {
return a[name] < b[name];
});
return function(keys, objs) {
if (arguments.length === 0) {throw new TypeError('cannot sort on nothing');}
var fns = R.map(function(key) {
return key.charAt(0) === "-" ?
R.pipe(R.comparator(propLt(R.substringFrom(1, key))), R.multiply(-1)) :
R.comparator(propLt(key));
}, keys);
var sorter = function(a, b) {
return R.reduce(function(acc, fn) {return acc || fn(a, b);}, 0, fns);
}
return arguments.length === 1 ? R.sort(sorter) : R.sort(sorter, objs);
};
}());
multisort(['name', '-number_of_reqs'], arr); //=> sorted clone
它是手动咖喱而不是调用R.curry
,因为创建单独的排序函数涉及相当多的工作,如果您使用相同的键集排序许多列表,则可以重复使用它们。如果这不是一个问题,可以稍微简化一下。
答案 3 :(得分:0)
如果您愿意为项目添加另一个依赖项,@panosoft/ramda-utils会附带一个compareProps函数,该函数完全符合原始问题的要求。
因此,根据您的原始示例,按预算降序排序,然后按名称排序,您可以执行以下操作:
var props = ["-budget", "name"];
var comparator = Ru.compareProps(props);
var sortedList = R.sort(comparator, arr);
答案 4 :(得分:-1)
使用javascript本地排序:
Array.prototype.multisort = function(columns) {
var arr = this;
arr.sort(function(a, b) {
return compare(a, b, 0);
});
function compare(a, b, colindex) {
if (colindex >= columns.length) return 0;
var columnname = columns[colindex];
var a_field1 = a[columnname];
var b_field1 = b[columnname];
var asc = (colindex % 2 === 0);
if (a_field1 < b_field1) return asc ? -1 : 1;
else if (a_field1 > b_field1) return asc ? 1 : -1;
else return compare(a, b, colindex + 1);
}
}
var arr = [{ name: 'Foo LLC', budget: 3500, number_of_reqs: 1040 },
{ name: '22nd Amendment',budget: 1500, number_of_reqs: 2000 },
{ name: 'STS 10', budget: 50000,number_of_reqs: 5000 },
{ name: 'STS 10', budget: 50000,number_of_reqs: 500 }];
arr.multisort(['name', 'number_of_reqs']);
if (window.console) window.console.log(arr);
&#13;