Chr*_*n R 113 javascript sorting underscore.js
我正在尝试使用基于多个属性的对象对数组进行排序.即,如果两个对象之间的第一个属性相同,则应使用第二个属性来共同映射这两个对象.例如,请考虑以下数组:
var patients = [
[{name: 'John', roomNumber: 1, bedNumber: 1}],
[{name: 'Lisa', roomNumber: 1, bedNumber: 2}],
[{name: 'Chris', roomNumber: 2, bedNumber: 1}],
[{name: 'Omar', roomNumber: 3, bedNumber: 1}]
];
Run Code Online (Sandbox Code Playgroud)
通过roomNumber属性i 对它们进行排序将使用以下代码:
var sortedArray = _.sortBy(patients, function(patient) {
return patient[0].roomNumber;
});
Run Code Online (Sandbox Code Playgroud)
这样可以正常工作,但我如何继续进行以便'John'和'Lisa'能够正确分类?
Ror*_*eod 248
sortBy 说它是一个稳定的排序算法,所以你应该能够先按你的第二个属性排序,然后再按你的第一个属性排序,如下所示:
var sortedArray = _(patients).chain().sortBy(function(patient) {
return patient[0].name;
}).sortBy(function(patient) {
return patient[0].roomNumber;
}).value();
Run Code Online (Sandbox Code Playgroud)
当第二个sortBy发现John和Lisa拥有相同的房间号码时,它会按照它们找到的顺序保留它们,第一个sortBy设置为"Lisa,John".
Dan*_*Tao 50
这是我在这些情况下有时会使用的一个hacky技巧:以可以排序结果的方式组合属性:
var sortedArray = _.sortBy(patients, function(patient) {
return [patient[0].roomNumber, patient[0].name].join("_");
});
Run Code Online (Sandbox Code Playgroud)
但是,正如我所说的那样,这非常hacky.要正确地执行此操作,您可能希望实际使用核心JavaScript sort方法:
patients.sort(function(x, y) {
var roomX = x[0].roomNumber;
var roomY = y[0].roomNumber;
if (roomX !== roomY) {
return compare(roomX, roomY);
}
return compare(x[0].name, y[0].name);
});
// General comparison function for convenience
function compare(x, y) {
if (x === y) {
return 0;
}
return x > y ? 1 : -1;
}
Run Code Online (Sandbox Code Playgroud)
当然,这将对您的阵列进行排序.如果你想要一个排序的副本(就像_.sortBy你会给你的那样),首先克隆数组:
function sortOutOfPlace(sequence, sorter) {
var copy = _.clone(sequence);
copy.sort(sorter);
return copy;
}
Run Code Online (Sandbox Code Playgroud)
出于无聊,我刚刚为此写了一个通用解决方案(按任意数量的键排序):看一看.
Mik*_*ney 28
我知道我已经迟到了,但是我想为那些需要那些已经建议的清洁和快速解决方案的人添加这个.您可以按最不重要的属性将sortBy调用链接到最重要的属性.在下面的代码创建的排序患者一个新的数组名称中RoomNumber从原来的阵列称为患者.
var sortedPatients = _.chain(patients)
.sortBy('Name')
.sortBy('RoomNumber')
.value();
Run Code Online (Sandbox Code Playgroud)
zob*_*fly 10
你的初始化患者有点奇怪,不是吗?为什么不初始化这个变量,因为这是一个真正的对象数组 - 你可以使用_.flatten()而不是单个对象数组的数组,也许它是错字问题):
var patients = [
{name: 'Omar', roomNumber: 3, bedNumber: 1},
{name: 'John', roomNumber: 1, bedNumber: 1},
{name: 'Chris', roomNumber: 2, bedNumber: 1},
{name: 'Lisa', roomNumber: 1, bedNumber: 2},
{name: 'Kiko', roomNumber: 1, bedNumber: 2}
];
Run Code Online (Sandbox Code Playgroud)
我对列表进行了不同的排序,并将Kiko添加到Lisa的床上; 只是为了好玩,看看会做些什么改变......
var sorted = _(patients).sortBy(
function(patient){
return [patient.roomNumber, patient.bedNumber, patient.name];
});
Run Code Online (Sandbox Code Playgroud)
检查排序,你会看到这个
[
{bedNumber: 1, name: "John", roomNumber: 1},
{bedNumber: 2, name: "Kiko", roomNumber: 1},
{bedNumber: 2, name: "Lisa", roomNumber: 1},
{bedNumber: 1, name: "Chris", roomNumber: 2},
{bedNumber: 1, name: "Omar", roomNumber: 3}
]
Run Code Online (Sandbox Code Playgroud)
所以我的答案是:在你的回调函数中使用一个数组
这与Dan Tao的答案非常相似,我只是忘记了连接(也许是因为我删除了独特项目的数组数组:))
使用你的数据结构,然后它将会 :
var sorted = _(patients).chain()
.flatten()
.sortBy( function(patient){
return [patient.roomNumber,
patient.bedNumber,
patient.name];
})
.value();
Run Code Online (Sandbox Code Playgroud)
并且测试负载会很有趣......
这些答案都不是理想的,因为它是在排序中使用多个字段的通用方法.上面的所有方法效率都很低,因为它们要么需要多次对数组进行排序(在足够大的列表中可能会使速度降低很多),或者它们会生成大量的垃圾对象,这些垃圾对象需要清理(最终会减慢)该计划下来).
这是一个快速,高效,易于反向排序的解决方案,可以与underscore或一起使用lodash,或直接使用Array.sort
最重要的部分是该compositeComparator方法,它采用比较器函数数组并返回一个新的复合比较器函数.
/**
* Chains a comparator function to another comparator
* and returns the result of the first comparator, unless
* the first comparator returns 0, in which case the
* result of the second comparator is used.
*/
function makeChainedComparator(first, next) {
return function(a, b) {
var result = first(a, b);
if (result !== 0) return result;
return next(a, b);
}
}
/**
* Given an array of comparators, returns a new comparator with
* descending priority such that
* the next comparator will only be used if the precending on returned
* 0 (ie, found the two objects to be equal)
*
* Allows multiple sorts to be used simply. For example,
* sort by column a, then sort by column b, then sort by column c
*/
function compositeComparator(comparators) {
return comparators.reduceRight(function(memo, comparator) {
return makeChainedComparator(comparator, memo);
});
}
Run Code Online (Sandbox Code Playgroud)
您还需要一个比较器功能来比较您希望排序的字段.该naturalSort函数将在给定特定字段的情况下创建比较器.为逆向排序编写比较器也很简单.
function naturalSort(field) {
return function(a, b) {
var c1 = a[field];
var c2 = b[field];
if (c1 > c2) return 1;
if (c1 < c2) return -1;
return 0;
}
}
Run Code Online (Sandbox Code Playgroud)
(到目前为止,所有代码都是可重用的,例如可以保存在实用程序模块中)
接下来,您需要创建复合比较器.对于我们的示例,它看起来像这样:
var cmp = compositeComparator([naturalSort('roomNumber'), naturalSort('name')]);
Run Code Online (Sandbox Code Playgroud)
这将按房间号排序,后跟名称.添加其他排序条件非常简单,不会影响排序的性能.
var patients = [
{name: 'John', roomNumber: 3, bedNumber: 1},
{name: 'Omar', roomNumber: 2, bedNumber: 1},
{name: 'Lisa', roomNumber: 2, bedNumber: 2},
{name: 'Chris', roomNumber: 1, bedNumber: 1},
];
// Sort using the composite
patients.sort(cmp);
console.log(patients);
Run Code Online (Sandbox Code Playgroud)
返回以下内容
[ { name: 'Chris', roomNumber: 1, bedNumber: 1 },
{ name: 'Lisa', roomNumber: 2, bedNumber: 2 },
{ name: 'Omar', roomNumber: 2, bedNumber: 1 },
{ name: 'John', roomNumber: 3, bedNumber: 1 } ]
Run Code Online (Sandbox Code Playgroud)
我更喜欢这种方法的原因是它允许在任意数量的字段上快速排序,不会产生大量垃圾或在排序中执行字符串连接,并且可以很容易地使用,以便某些列反向排序,而顺序列使用自然分类.
来自http://janetriley.net/2014/12/sort-on-multiple-keys-with-underscores-sortby.html 的简单示例(@MikeDevenney 提供)
代码
var FullySortedArray = _.sortBy(( _.sortBy(array, 'second')), 'first');
Run Code Online (Sandbox Code Playgroud)
使用您的数据
var FullySortedArray = _.sortBy(( _.sortBy(patients, 'roomNumber')), 'name');
Run Code Online (Sandbox Code Playgroud)