javascript - 如何在 JavaScript 中对对象集合进行排序而不将其转换为数组

标签 javascript underscore.js

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

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 = {};

所以有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.

15.2.3.7 (和 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.

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

也就是说,Underscore 的 sortBy基本上是一个 Schwartzian Transform结合标准 JavaScript sort和下划线的 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 保持同步,但如果您将它们都隐藏在一个对象后面,这并不难。

关于javascript - 如何在 JavaScript 中对对象集合进行排序而不将其转换为数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10623463/

相关文章:

javascript - 避免在 <a href =""> 元素上使用 jQuery click 函数

javascript - 如何使用 CasperJS 与自定义选择框交互?

javascript - AJAX 页面重新加载

javascript - <div> 内容未在 &lt;iframe&gt; 中加载

javascript - 如何在 javascript 或 lodash 中将每个数组元素转换为单独的数组

javascript - 跨度垂直对齐

javascript - 如何创建一个 memoize 函数

javascript - 如何使用 underscore.js 将同一对象克隆为三个对象

node.js - 如何在underscore js中结合使用after和each来创建同步循环

javascript - 在 Javascript/React 中映射 Bootstrap 列中的数组