Об’єкти проти масивів у 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'}
};

Другий чи третій варіант - це, очевидно, шлях, якщо у вас є (або ви очікуєте, що у вас може бути) більше однієї "ціннісної" частини для зберігання (наприклад, додавання за їх віком або щось інше), тому, для аргументу, припустимо, що в цій структурі ніколи більше не буде значень даних, необхідних. Який з них ви обираєте і чому?


Редагувати : Приклад тепер показує найпоширенішу ситуацію: непослідовні ідентифікатори.


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

Відповіді:


58

Кожне рішення має свої варіанти використання.

Я думаю, що перше рішення є хорошим, якщо ви намагаєтеся визначити взаємозв'язок "один на один" (наприклад, просте відображення), особливо якщо вам потрібно використовувати ключ як ключ пошуку.

Друге рішення мені здається найнадійнішим загалом, і я, мабуть, скористався ним, якщо мені не потрібен ключ швидкого пошуку:

  • Це самоопис, тому вам не потрібно залежати від того, хто використовує людей, щоб знати, що ключовим є ідентифікатор користувача.
  • Кожен об’єкт є автономним, що краще для передачі даних в іншому місці - замість двох параметрів (ідентифікатор та ім’я) ви просто передаєте людям.
  • Це рідкісна проблема, але іноді значення ключів можуть бути неприпустимими для використання в якості ключів. Наприклад, одного разу я хотів зіставити перетворення рядків (наприклад, ":" у ">"), але оскільки ":" не є дійсним ім'ям змінної, мені довелося використовувати другий метод.
  • Це легко розширюється, у випадку, якщо десь уздовж лінії потрібно додати більше даних деяким (або всім) користувачам. (Вибачте, я знаю про ваше "заради аргументу", але це важливий аспект.)

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


1
на вашому третьому пункті там. Щоб уникнути цієї проблеми, властивості об’єктів можна отримати в позначеннях дужок: var x = {"ab> c ++": "foo"}; попередження (x ['ab> c ++']);
nickf

Я вважаю, у вас проблема кількох зарезервованих слів із властивостями. Але жодних проблем при використанні цифр.
derobert

4
@derobert: Я не думаю. x = {"видалити": 1, "для": 2, "функція": 3}; - це дійсно і доступ до нього можна отримати таким же чином.
nickf

7

Насправді є четвертий варіант:

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

оскільки ваші цінності бувають послідовними. (Звичайно, вам доведеться додати / відняти один --- або просто поставити undefined як перший елемент).

Особисто я піду з вашим (1) або (3), тому що це буде найшвидший пошук когось за ідентифікатором (O log n у гіршому випадку). Якщо вам потрібно знайти ідентифікатор 3 у (2), ви можете або шукати його за індексом (у цьому випадку мій (4) - це нормально), або вам потрібно шукати - O (n).

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

Редагувати з nickf: З тих пір я змінив приклад в OP, тому ця відповідь може більше не мати такого сенсу. Вибачення.

Після редагування

Гаразд, після редагування, я вибрав би варіант (3). Він є розширюваним (легко додавати нові атрибути), має швидкі пошуки, і його також можна повторити. Це також дозволяє вам повернутися від входу назад до посвідчення особи, якщо вам це потрібно.

Варіант (1) був би корисний, якщо (а) вам потрібно економити пам’ять; (б) вам ніколи не потрібно переходити від об'єкта назад до ідентифікатора; (c) ви ніколи не продовжите збережені дані (наприклад, ви не можете додати прізвище людини)

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


Я розумію, чому (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]; 'який дуже добре працює для швидкого внутрішнього зберігання даних у будь-якому класі - ну, під класом я маю на увазі "нову WhateverFunction ();" річ, яка Javascripters не любить, мені подобається; -) ...
Маркус

... Але це працює лише в тому випадку, якщо ідентифікатор є цілим числом, інакше "класичне" рішення 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

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

Простота структури означає, що її найшвидше розбирати, і у випадку невеликих, рідко (або ніколи) мінливих наборів даних, таких як цей, я можу лише уявити, що вона буде часто виконуватися - в цьому випадку мінімальними накладними витратами є шлях.


2

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

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

Він використовує

  • об'єкт для зберігання ключів, що дозволяє швидко видаляти та оновлювати значення операцій та
  • зв’язаний список, щоб забезпечити дуже швидку ітерацію значення

Сподіваюся, це допоможе :)


чому б не додати посилання jsperf?
Janus Troelsen

ця бібліотека - це чудова робота. Просто врятував мені купу часу.
Кріс Хаф,

1

Третій варіант є найкращим для будь-якої перспективної програми. Можливо, ви захочете додати більше полів до запису вашої особи, тому перший варіант не підходить. Крім того, дуже ймовірно, що у вас буде велика кількість людей для зберігання, і ви захочете швидко шукати записи - отже, скидання їх у простий масив (як це зроблено у варіанті №2) теж не є хорошою ідеєю.

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

Одне, чого не вистачає варіанту №3, - це стабільне детерміноване впорядкування (що є перевагою варіанту №2). Якщо вам це потрібно, я б рекомендував зберігати упорядкований масив ідентифікаторів осіб як окрему структуру, коли вам потрібно перерахувати осіб по порядку. Перевагою було б те, що ви можете зберігати кілька таких масивів для різних впорядкувань одного і того ж набору даних.


0

Враховуючи ваше обмеження, що у вас як і раніше буде лише ім'я, я вибрав би перший варіант. Це найчистіший, має найменше накладних витрат і найшвидший пошук.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.