我试图避免为以下用例编写自己的排序算法:
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));
这是控制台输出:
正如您所看到的,使用非核心的排序我正在丢失关键数据。这个结构可以变得非常大,所以我试图避免像转换为数组然后回到集合之类的东西。如果不滚动我自己的排序函数,有没有办法做到这一点?
答案 0 :(得分:26)
您的avatars
不是数组,它只是一个对象:
avatars = {};
所以有no defined order for its elements:
未指定枚举属性的机制和顺序(第一个算法中的步骤6.a,第二个算法中的步骤7.a)。
如果实现为for-in语句定义了特定的枚举顺序,则必须使用相同的枚举顺序对此算法的步骤3中的列表元素进行排序。
您还可以检查section 8.6以查看是否有关于对象中属性顺序的提及。对对象属性进行排序的唯一要求是,如果实现在任何地方定义了一个订单,那么它必须在任何地方使用相同的排序,但是如果那么这是一个很大的。大多数实现可能使用对象键的插入顺序,但我找不到任何需要它们的东西(如果有人可以指出定义对象键的任何特定顺序的规范中的任何内容,我会感激评论。)
也就是说,Underscore的sortBy
基本上是Schwartzian Transform与标准JavaScript sort
和Underscore的pluck
相结合来打开Schwartzian变换备忘录包装器; pluck
返回一个数组,因此sortBy
也返回一个数组。因此,您的最终_.keys(avatars)
调用实际上是在数组上调用_.keys
;数组的键(AKA可枚举属性)是数组的索引,它们是从零开始的连续整数。
您使用的是错误的数据结构。如果你需要一个稀疏数组,但也需要像数组一样操作它(即排序它),那么你应该将索引放在对象中并使用普通数组而不是pluck
而不是keys
:< / p>
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
提供您正在寻找的直接访问权限。当然,您必须保持avatars
和avatars_by_idx
同步,但如果将它们隐藏在对象后面则不是非常困难。