如何在JavaScript中对对象集合进行排序而不将其转换为数组 [英] How to sort a collection of objects in JavaScript without converting it to an array

查看:107
本文介绍了如何在JavaScript中对对象集合进行排序而不将其转换为数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图避免针对以下用例编写自己的排序算法:

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

这是控制台输出:

  • ["102","100","101"]
  • ["0","1","2"]

如您所见,使用undescore的sortBy,我丢失了关键数据.这个结构可能会变得非常大,所以我试图避免诸如转换为数组然后再返回到集合之类的事情.有什么方法可以不用滚动我自己的排序功能吗?

解决方案

您的avatars不是数组,而只是一个对象:

avatars = {};

因此,其元素没有定义的顺序:

未指定枚举属性的机制和顺序(第一个算法中的步骤6.a,第二个算法中的步骤7.a).

15.2.3.7 (和15.2.3.14 ):

如果实现为for-in语句定义了特定的枚举顺序,则必须使用相同的枚举顺序对此算法的第3步中的列表元素进行排序.

您还可以查看第8.6 节,以查看是否有关于对象中属性顺序的任何提及.对象属性排序的唯一要求是,如果实现在任何地方都定义了一个顺序,则它必须在任何地方都使用相同的排序,但这是一个很大的 if .大多数实现可能都将插入顺序用于对象的键,但是我找不到任何需要它们插入的东西(如果有人可以在规范中指出定义对象键的任何特定顺序的任何内容,我将不胜感激).

也就是说,Underscore的 sortBy 基本上是 sort 和Underscore的 pluck 来解开Schwartzian Transform备忘录包装; pluck返回一个数组,所以sortBy也返回一个数组.因此,您最后的_.keys(avatars)调用实际上是在数组上调用 _.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'));

演示: http://jsfiddle.net/ambiguous/UCWL2/

如果您还需要通过idx快速访问,则可以为直接idx访问设置并行对象:

var avatars_by_idx = { };
for(var i = 0; i < avatars.length; ++i)
    avatars_by_idx[avatars[i].idx] = avatars[i];

然后avatars_by_idx提供您正在寻找的直接访问权限.当然,您必须保持avatarsavatars_by_idx同步,但是如果将它们都隐藏在一个对象后面,这并不是很困难.

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

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?

解决方案

Your avatars is not an Array, it is just an object:

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.

and 15.2.3.7 (and 15.2.3.14):

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.

You can also check section 8.6 to 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).

That said, Underscore's sortBy is basically a Schwartzian Transform combined 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.

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:

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/

If you also need quick access by idx then you could set up a parallel object for direct idx access:

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.

这篇关于如何在JavaScript中对对象集合进行排序而不将其转换为数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆