如何在 JavaScript 中对对象集合进行排序而不将其转换为数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10623463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to sort a collection of objects in JavaScript without converting it to an array
提问by Rosco
I am trying to avoid writing my own sorting algorithm for the following use case:
我试图避免为以下用例编写自己的排序算法:
avatars = {};
avatars[102] = {userInfo: {buddy_name: 'Avatar102', is_online: 1}};
avatars[100] = {userInfo: {buddy_name: 'Avatar100', is_online: 1}};
avatars[101] = {userInfo: {buddy_name: 'Avatar101', is_online: 1}};
console.log(_.keys(avatars));
avatars = _.sortBy(avatars, function(avatar) {return avatar.userInfo.buddy_name.toLowerCase();});
console.log(_.keys(avatars));
Here's the console output:
这是控制台输出:
- ["102", "100", "101"]
- ["0", "1", "2"]
- [“102”、“100”、“101”]
- [“0”、“1”、“2”]
As you can see, with undescore's sortBy I am losing the key data. This struct can get very large, so I am trying to avoid things like converting to an array and then back to the collection. Is there any way to do this without rolling my own sort function?
如您所见,使用 undescore 的 sortBy 我丢失了关键数据。这个结构可能会变得非常大,所以我试图避免像转换为数组然后再返回到集合这样的事情。有没有办法在不滚动我自己的排序功能的情况下做到这一点?
回答by mu is too short
Your avatars
is not an Array, it is just an object:
你avatars
不是一个数组,它只是一个对象:
avatars = {};
so there is no defined order for its elements:
所以它的元素没有定义的顺序:
The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified.
枚举属性的机制和顺序(第一个算法中的步骤 6.a,第二个算法中的步骤 7.a)没有指定。
If an implementation defines a specific order of enumeration for the for-in statement, that same enumeration order must be used to order the list elements in step 3 of this algorithm.
如果实现为 for-in 语句定义了特定的枚举顺序,则必须使用相同的枚举顺序在此算法的第 3 步中对列表元素进行排序。
You can also check section 8.6to see if there is any mention about the order of properties in an object. The only requirement for ordering of object properties is that if the implementation defines an order anywhere then it has to use that same ordering everywhere but that's a big if. Most implementations probably use insertion order for an object's keys but I can't find anything that requires them to (I'd appreciate a comment if anyone can point out anything in the specs that define any particular order of an object's keys).
您还可以查看第 8.6 节以查看是否有任何关于对象中属性顺序的提及。对象属性排序的唯一要求是,如果实现在任何地方定义了一个顺序,那么它必须在任何地方使用相同的顺序,但这是一个很大的if。大多数实现可能对对象的键使用插入顺序,但我找不到任何需要它们的东西(如果有人能指出规范中定义对象键的任何特定顺序的任何内容,我将不胜感激)。
That said, Underscore's sortBy
is basically a Schwartzian Transformcombined with a standard JavaScript sort
and Underscore's pluck
to unwrap the Schwartzian Transform memo wrappers; pluck
returns an array so sortBy
also returns an array. Hence, your final _.keys(avatars)
call is actually calling _.keys
on an array; the keys of an array (AKA enumerable properties) are the array's indices and those are consecutive integers starting at zero.
也就是说,UnderscoresortBy
基本上是一个Schwartzian Transform结合标准 JavaScriptsort
和 Underscorepluck
来解开 Schwartzian Transform 备忘录包装器;pluck
返回一个数组,所以sortBy
也返回一个数组。因此,您的最终_.keys(avatars)
调用实际上是调用_.keys
一个数组;数组的键(也称为可枚举属性)是数组的索引,它们是从零开始的连续整数。
You're using the wrong data structure. If you need a sparse array but also need to manipulate it like an array (i.e. sort it), then you should put the indexes inside the objects and use a normal array and pluck
instead of keys
:
您使用了错误的数据结构。如果您需要一个稀疏数组,但也需要像数组一样操作它(即对其进行排序),那么您应该将索引放在对象中并使用普通数组而pluck
不是keys
:
var avatars = [
{idx: 102, userInfo: {buddy_name: 'Avatar102', is_online: 1}},
{idx: 100, userInfo: {buddy_name: 'Avatar100', is_online: 1}},
{idx: 101, userInfo: {buddy_name: 'Avatar101', is_online: 1}}
];
console.log(_(avatars).pluck('idx'));
avatars = _(avatars).sortBy(function(avatar) {
return avatar.userInfo.buddy_name.toLowerCase();
});
console.log(_(avatars).pluck('idx'));
Demo: http://jsfiddle.net/ambiguous/UCWL2/
演示:http: //jsfiddle.net/ambiguous/UCWL2/
If you also need quick access by idx
then you could set up a parallel object for direct idx
access:
如果您还需要快速访问,idx
那么您可以设置一个并行对象以进行直接idx
访问:
var avatars_by_idx = { };
for(var i = 0; i < avatars.length; ++i)
avatars_by_idx[avatars[i].idx] = avatars[i];
Then avatars_by_idx
provides the direct access you're looking for. Of course, you'd have to keep avatars
and avatars_by_idx
synchronized but that's not terribly difficult if you hide them both behind an object.
然后avatars_by_idx
提供您正在寻找的直接访问。当然,您必须保持avatars
和avatars_by_idx
同步,但如果您将它们都隐藏在一个对象后面,这并不是非常困难。