Подтвердить что ты не робот

Как отсортировать коллекцию объектов в 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 я теряю ключевые данные. Эта структура может стать очень большой, поэтому я стараюсь избегать таких вещей, как преобразование в массив, а затем обратно в коллекцию. Есть ли способ сделать это, не выполняя мою собственную функцию сортировки?

4b9b3361

Ответ 1

Ваш avatars не является массивом, это просто объект:

avatars = {};

поэтому не определен порядок для его элементов:

Механика и порядок перечисления свойств (шаг 6.a в первом алгоритме, шаг 7.a во втором) не указывается.

и 15.2.3.715.2.3.14):

Если реализация определяет определенный порядок перечисления для оператора for-in, тот же порядок перечисления должен использоваться для упорядочения элементов списка на шаге 3 этого алгоритма.

Вы также можете проверить раздел 8.6, чтобы узнать, есть ли упоминание о порядке свойств объекта. Единственное требование для упорядочения свойств объекта состоит в том, что если реализация определяет порядок где угодно, тогда он должен использовать тот же порядок везде, но большой, если. Большинство реализаций, вероятно, используют порядок вставки для ключей объектов, но я не могу найти что-либо, что требует от них (я был бы признателен за комментарий, если кто-нибудь может указать что-либо в спецификациях, которые определяют какой-либо конкретный порядок ключей объекта).

Тем не менее, Underscore sortBy в основном представляет собой Schwartzian Transform в сочетании со стандартным JavaScript sort и Underscore pluck, чтобы развернуть обертки для заметок Schwartzian Transform; pluck возвращает массив, поэтому sortBy также возвращает массив. Следовательно, ваш окончательный вызов _.keys(avatars) фактически вызывает _.keys в массиве; ключи массива (перечислимые свойства AKA) - это индексы массива, а последовательные целые числа начинаются с нуля.

Вы используете неправильную структуру данных. Если вам нужен разреженный массив, но также нужно манипулировать им, как массив (т.е. Сортировать его), тогда вы должны поместить индексы внутри объектов и использовать обычный массив и 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 предоставляет прямой доступ, который вы ищете. Конечно, вам нужно будет синхронизировать avatars и avatars_by_idx, но это не так сложно, если вы спрячете их как за объектом.