Объекты против массивов в Javascript для пар ключ / значение

83

Скажем, у вас очень простая структура данных:

(personId, name)

... и вы хотите сохранить некоторые из них в переменной javascript. На мой взгляд, у вас есть три варианта:

// a single object
var people = {
    1 : 'Joe',
    3 : 'Sam',
    8 : 'Eve'
};

// or, an array of objects
var people = [
    { id: 1, name: 'Joe'},
    { id: 3, name: 'Sam'},
    { id: 8, name: 'Eve'}
];

// or, a combination of the two
var people = {
    1 : { id: 1, name: 'Joe'},
    3 : { id: 3, name: 'Sam'},
    8 : { id: 8, name: 'Eve'}
};

Второй или третий вариант, очевидно, подходит, если у вас есть (или вы ожидаете, что у вас может быть) более одной части «значения» для хранения (например, добавление их возраста или что-то в этом роде), поэтому, в целях аргументации, предположим, что в этой структуре больше никогда не будет никаких значений данных. Какой из них вы выберете и почему?


Изменить : в примере теперь показана наиболее распространенная ситуация: непоследовательные идентификаторы.

Nickf
источник
1
Также есть 2d-массив: var people = [[1, 'Joe'],[3, 'Sam'],[8,'Eve']];(который не дает вам быстрого поиска по идентификатору, но является еще одним вариантом для хранения данных)
Пользователь

Ответы:

58

У каждого решения есть свои варианты использования.

Я думаю, что первое решение хорошо, если вы пытаетесь определить отношения «один к одному» (например, простое сопоставление), особенно если вам нужно использовать ключ в качестве ключа поиска.

Второе решение в целом кажется мне наиболее надежным, и я бы, вероятно, использовал его, если бы мне не понадобился ключ быстрого поиска:

  • Это самоописание, поэтому вам не нужно зависеть от кого-либо, кто использует людей, чтобы знать, что ключ - это идентификатор пользователя.
  • Каждый объект является автономным, что лучше для передачи данных в другое место - вместо двух параметров (id и name) вы просто передаете людям.
  • Это редкая проблема, но иногда значения ключей могут быть недопустимыми для использования в качестве ключей. Например, однажды я хотел отобразить преобразования строк (например, «:» в «>»), но поскольку «:» не является допустимым именем переменной, мне пришлось использовать второй метод.
  • Его легко расширить, если где-то в процессе вам нужно добавить больше данных для некоторых (или всех) пользователей. (Извините, я знаю, что вы «для аргументации», но это важный аспект.)

Третий вариант подойдет, если вам нужно быстрое время поиска + некоторые из перечисленных выше преимуществ (передача данных, самоописание). Однако, если вам не нужно быстрое время поиска, это намного сложнее. Кроме того, в любом случае вы рискуете ошибиться, если идентификатор объекта каким-либо образом отличается от идентификатора людей .

Дэн Лью
источник
1
по вашему третьему пункту. Чтобы избежать этой проблемы, к свойствам объектов можно обращаться в скобках: var x = {"ab> c ++": "foo"}; alert (x ['ab> c ++']);
nickf
Я полагаю, что у вас действительно есть проблема с несколькими зарезервированными словами со свойствами. Но никаких проблем при использовании цифр.
derobert
4
@derobert: Я так не думаю. x = {"удалить": 1, "для": 2, "функция": 3}; - это действительно так, и к нему можно получить доступ таким же образом.
nickf
7

Собственно, есть четвертый вариант:

var people = ['Joe', 'Sam', 'Eve'];

поскольку ваши значения совпадают. (Конечно, вам придется добавить / вычесть один --- или просто поставить undefined в качестве первого элемента).

Лично я бы с вами (1) или (3), потому что это будет самым быстрым , чтобы посмотреть кого - то с помощью ID (O журнала п в худшем случае). Если вам нужно найти id 3 в (2), вы можете либо найти его по индексу (в этом случае my (4) в порядке), либо вам нужно выполнить поиск - O (n).

Уточнение: я говорю, что O (log n ) - худшее из возможных, потому что AFAIK и реализация могут решить использовать сбалансированное дерево вместо хеш-таблицы. Хеш-таблица будет O (1), предполагая минимальные коллизии.

Изменить из nickf: с тех пор я изменил пример в OP, поэтому этот ответ может больше не иметь такого большого смысла. Извинения.

Постредактировать

Хорошо, постредактируйте, я бы выбрал вариант (3). Он расширяемый (легко добавлять новые атрибуты), обеспечивает быстрый поиск, а также может повторяться. Это также позволяет вам вернуться от входа к ID, если вам нужно.

Вариант (1) будет полезен, если (а) вам нужно сэкономить память; (б) вам никогда не нужно возвращаться от объекта к id; (c) вы никогда не будете расширять сохраненные данные (например, вы не можете добавить фамилию человека)

Вариант (2) хорош, если вам (а) нужно сохранить порядок; (б) необходимо перебрать все элементы; (c) не нужно искать элементы по идентификатору, если они не отсортированы по идентификатору (вы можете выполнить двоичный поиск в O (log n ). Обратите внимание, конечно, если вам нужно сохранить его отсортированным, вы заплатите стоимость на вставке.

Derobert
источник
Я понимаю, почему (1) и (3) быстрее, чем (2), но мне интересно, как вы получили O (log (n)) за это время? Разве это не должно быть O (1), поскольку идентификаторы должны храниться в хеш-таблице?
Натаниэль Рейнхарт,
Я назвал log n как худшее, потому что я видел, как реализация решает использовать сбалансированное дерево вместо хеш-таблицы. Хеш-таблица, конечно, будет O (1) [при условии минимальных коллизий].
derobert
идентификаторы не всегда так просты, и они редко бывают последовательными IRL, поэтому вам необходимо их указать. Я признаю, что это был плохой / неоднозначный пример.
nickf
+1, ваш ответ не хуже принятого ... но ваше четвертое решение не хранит все данные, поэтому вот правильный четвертый вариант: var people = []; people[1] = 'Joe'; people[3] = 'Sam'; people[8] = 'Eve';или в виде двумерного массива: var people = []; people[1] = ['Joe', 'Smith']; people[3] = ['Sam', 'Miller']; people[8] = ['Eve', 'Sweet'];(будут доступны, например, 'people [ 3] [1]; ', который очень хорошо работает для быстрого внутреннего хранения данных внутри любого класса - ну, под классом я подразумеваю «new WhateverFunction ();», что не нравится Javascripters, а мне нравится; -) ...
Маркус
... Но это работает только в том случае, если id является целым числом, в противном случае "классическое" решение 2 (вместе со знаменитым for(var key in object)доступом) - это путь в обычных случаях (он имеет O (n / 2)) или решение 3, если у вас действительно большой массив для более быстрого доступа, потому что он имеет O (1) ... и, наконец, давайте забудем о приведенном выше решении 1, не только если ваш идентификатор является целым числом, но также и в случае, если идентификатор не является целым числом: var people = {'Smith':'Joe', 'Miller':'Sam', 'Sweet': 'Eve' };( потому что вам нужно будет получить доступ к своему объекту следующим образом: alert(people.Smith);что не очень приятно!).
Маркус
2

Предполагая, что данные никогда не изменятся, первый вариант (с одним объектом) является лучшим.

Простота структуры означает, что это самый быстрый синтаксический анализ, а в случае небольших, редко (или никогда) изменяющихся наборов данных, таких как этот, я могу только представить, что он будет часто выполняться - и в этом случае минимальные накладные расходы - это путь идти.

karim79
источник
2

Я создал небольшую библиотеку для управления парами ключ-значение.

https://github.com/scaraveos/keyval.js#readme

Оно использует

  • объект для хранения ключей, который позволяет выполнять операции быстрого удаления и извлечения значений и
  • связанный список для очень быстрой итерации значений

Надеюсь, поможет :)

Скаравео
источник
почему бы не добавить ссылку jsperf?
Janus Troelsen
эта библиотека потрясающая, отличная работа. Просто сэкономил мне кучу времени.
Крис Хаф
1

Третий вариант лучше всего подходит для любого перспективного приложения. Возможно, вы захотите добавить больше полей в свою личную запись, поэтому первый вариант вам не подходит. Кроме того, весьма вероятно, что у вас будет большое количество людей для хранения, и вы захотите быстро искать записи - поэтому выгрузка их в простой массив (как это сделано в варианте № 2) тоже не является хорошей идеей.

Третий шаблон дает вам возможность использовать любую строку в качестве идентификатора, иметь сложные структуры Person и получать и устанавливать записи о людях за постоянное время. Это определенно правильный путь.

Единственное, чего не хватает варианту №3, так это стабильного детерминированного упорядочивания (что является преимуществом варианта №2). Если вам это нужно, я бы рекомендовал сохранить упорядоченный массив идентификаторов людей в виде отдельной структуры на тот случай, когда вам нужно перечислить людей по порядку. Преимущество состоит в том, что вы можете хранить несколько таких массивов для разного порядка одного и того же набора данных.

левик
источник
0

Учитывая ваше ограничение, что в качестве значения вы всегда будете использовать только имя, я бы выбрал первый вариант. Он самый чистый, с наименьшими накладными расходами и самым быстрым взглядом.

Натаниэль Рейнхарт
источник