Objekte gegen Arrays in Javascript für Schlüssel / Wert-Paare

81

Angenommen, Sie haben eine sehr einfache Datenstruktur:

(personId, name)

... und Sie möchten eine Reihe davon in einer Javascript-Variablen speichern. Aus meiner Sicht haben Sie drei Möglichkeiten:

// 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'}
};

Die zweite oder dritte Option ist offensichtlich der richtige Weg, wenn Sie mehr als einen "Wert" -Teil zum Speichern haben (oder erwarten, dass Sie möglicherweise haben) (z. B. Hinzufügen in ihrem Alter oder so etwas). Nehmen wir an, dass in dieser Struktur niemals mehr Datenwerte benötigt werden. Welches wählen Sie und warum?


Bearbeiten : Das Beispiel zeigt jetzt die häufigste Situation: nicht sequentielle IDs.

nickf
quelle
1
Es gibt auch ein 2d-Array: var people = [[1, 'Joe'],[3, 'Sam'],[8,'Eve']];(das Ihnen keine schnelle Suche nach ID ermöglicht, aber eine weitere Option zum Speichern der Daten ist)
Benutzer

Antworten:

58

Jede Lösung hat ihre Anwendungsfälle.

Ich denke, die erste Lösung ist gut, wenn Sie versuchen, eine Eins-zu-Eins-Beziehung zu definieren (z. B. eine einfache Zuordnung), insbesondere wenn Sie den Schlüssel als Suchschlüssel verwenden müssen.

Die zweite Lösung fühlt sich für mich im Allgemeinen am robustesten an, und ich würde sie wahrscheinlich verwenden, wenn ich keinen schnellen Suchschlüssel benötige:

  • Es ist selbstbeschreibend, sodass Sie sich nicht darauf verlassen müssen, dass jemand Personen verwendet, um zu wissen, dass der Schlüssel die ID des Benutzers ist.
  • Jedes Objekt ist in sich geschlossen, was besser für die Weitergabe der Daten an eine andere Stelle ist - anstelle von zwei Parametern (ID und Name) geben Sie nur Personen weiter.
  • Dies ist ein seltenes Problem, aber manchmal sind die Schlüsselwerte möglicherweise nicht gültig, um als Schlüssel verwendet zu werden. Zum Beispiel wollte ich einmal Zeichenfolgenkonvertierungen zuordnen (z. B. ":" zu ">"), aber da ":" kein gültiger Variablenname ist, musste ich die zweite Methode verwenden.
  • Es ist leicht erweiterbar, falls Sie irgendwo auf der Linie einigen (oder allen) Benutzern mehr Daten hinzufügen müssen. (Entschuldigung, ich weiß von Ihrem "um der Argumentation willen", aber dies ist ein wichtiger Aspekt.)

Der dritte wäre gut, wenn Sie eine schnelle Suchzeit + einige der oben aufgeführten Vorteile benötigen (Weitergabe der Daten, Selbstbeschreibung). Wenn Sie jedoch keine schnelle Suchzeit benötigen, ist dies viel umständlicher. In beiden Fällen besteht außerdem die Gefahr eines Fehlers, wenn die ID im Objekt irgendwie von der ID in Personen abweicht .

Dan Lew
quelle
1
auf Ihren dritten Punkt dort. Auf Objekteigenschaften kann in Klammernotation zugegriffen werden, um dieses Problem zu vermeiden: var x = {"ab> c ++": "foo"}; alert (x ['ab> c ++']);
Nickf
Ich glaube, Sie haben das Problem einiger reservierter Wörter mit Eigenschaften. Aber keine Probleme, wenn Sie Zahlen verwenden.
Derobert
4
@derobert: Das glaube ich nicht. x = {"delete": 1, "for": 2, "function": 3}; - Dies ist gültig und kann auf die gleiche Weise abgerufen werden.
Nickf
7

Eigentlich gibt es eine vierte Option:

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

da Ihre Werte zufällig aufeinander folgen. (Natürlich müssen Sie eins addieren / subtrahieren - oder einfach undefiniert als erstes Element setzen).

Persönlich würde ich mit Ihrer (1) oder (3) gehen, weil diese am schnellsten jemanden nach ID suchen (O log n im schlimmsten Fall). Wenn Sie die ID 3 in (2) finden müssen, können Sie sie entweder nach Index nachschlagen (in diesem Fall ist meine (4) in Ordnung) oder Sie müssen nach - O (n) suchen.

Klarstellung: Ich sage, O (log n ) ist das Schlimmste, was es sein könnte, weil AFAIK und die Implementierung entscheiden könnten, einen ausgeglichenen Baum anstelle einer Hash-Tabelle zu verwenden. Eine Hash-Tabelle wäre O (1) unter der Annahme minimaler Kollisionen.

Edit from nickf: Ich habe das Beispiel seitdem im OP geändert, daher macht diese Antwort möglicherweise nicht mehr so ​​viel Sinn. Entschuldigung.

Hat es geposted

Ok, nachbearbeiten, ich würde Option (3) auswählen. Es ist erweiterbar (einfach, neue Attribute hinzuzufügen), bietet schnelle Suchvorgänge und kann auch iteriert werden. Außerdem können Sie bei Bedarf vom Eintrag zurück zur ID wechseln.

Option (1) wäre nützlich, wenn (a) Sie Speicherplatz sparen müssen; (b) Sie müssen niemals vom Objekt zurück zur ID gehen; (c) Sie werden die gespeicherten Daten niemals erweitern (z. B. können Sie den Nachnamen der Person nicht hinzufügen).

Option (2) ist gut, wenn Sie (a) die Bestellung beibehalten müssen; (b) müssen alle Elemente iterieren; (c) Sie müssen Elemente nicht nach ID suchen, es sei denn, sie sind nach ID sortiert (Sie können eine binäre Suche in O (log n ) durchführen. Beachten Sie natürlich, dass Sie zahlen müssen, wenn Sie sie sortiert halten müssen Kosten auf Beilage.

derobert
quelle
Ich verstehe, warum (1) und (3) schneller sind als (2), aber ich frage mich, wie Sie für die Zeit O (log (n)) bekommen haben? Sollte es nicht O (1) sein, da die IDs in einer Hash-Tabelle gespeichert werden sollten?
Nathaniel Reinhart
Ich habe log n als schlechtest angegeben, da ich eine Implementierung sehen konnte, die sich entschied, einen ausgeglichenen Baum anstelle einer Hash-Tabelle zu verwenden. Eine Hash-Tabelle wäre natürlich O (1) [vorausgesetzt, die Kollisionen sind minimal].
Derobert
Die IDs sind nicht immer so einfach und sie sind selten sequentielle IRL, weshalb Sie sie angeben müssen. Ich gebe zu, dass es ein schlechtes / mehrdeutiges Beispiel war.
Nickf
+1, Ihre Antwort ist nicht schlechter als die akzeptierte ... aber Ihre vierte Lösung speichert nicht alle Daten. Hier ist die richtige vierte Option: var people = []; people[1] = 'Joe'; people[3] = 'Sam'; people[8] = 'Eve';oder als zweidimensionales Array: var people = []; people[1] = ['Joe', 'Smith']; people[3] = ['Sam', 'Miller']; people[8] = ['Eve', 'Sweet'];(wird z. 3] [1]; 'was sehr gut für die schnelle interne Datenspeicherung in jeder Klasse funktioniert - nun, mit Klasse meine ich die "neue WhateverFunction ();" - Sache, die Javascripters nicht mag, ich tue; -) ...
Marcus
... Dies funktioniert jedoch nur, wenn die ID eine Ganzzahl ist. Andernfalls ist die "klassische" Lösung 2 (zusammen mit der for(var key in object)für den Zugriff bekannten ) in normalen Fällen (mit O (n / 2)) oder Lösung der richtige Weg 3 Wenn Sie ein wirklich großes Array für einen schnelleren Zugriff haben, weil es O (1) hat ... und vergessen wir schließlich die obige Lösung 1, nicht nur, wenn Ihre ID eine Ganzzahl ist, sondern auch für den Fall, dass die ID keine Ganzzahl ist: var people = {'Smith':'Joe', 'Miller':'Sam', 'Sweet': 'Eve' };( weil du so auf dein Objekt zugreifen müsstest: alert(people.Smith);was nicht wirklich schön ist!).
Marcus
2

Vorausgesetzt, die Daten ändern sich nie, ist die erste Option (Einzelobjekt) die beste.

Die Einfachheit der Struktur bedeutet, dass sie am schnellsten analysiert werden kann. Bei kleinen, selten (oder nie) sich ändernden Datensätzen wie diesem kann ich mir nur vorstellen, dass sie häufig ausgeführt werden. In diesem Fall ist der minimale Overhead der weit weg.

karim79
quelle
2

Ich habe eine kleine Bibliothek erstellt, um Schlüsselwertpaare zu verwalten.

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

Es verwendet

  • ein Objekt zum Speichern der Schlüssel, das schnelles Löschen und Abrufen von Werten ermöglicht und
  • Eine verknüpfte Liste, um eine wirklich schnelle Wertiteration zu ermöglichen

Ich hoffe es hilft :)

Scaraveos
quelle
Warum nicht einen jsperf-Link hinzufügen?
Janus Troelsen
Diese Bibliothek ist großartig, großartige Arbeit. Ich habe mir nur ein bisschen Zeit gespart.
Chris Hough
1

Die dritte Option ist die beste für jede zukunftsgerichtete Anwendung. Möglicherweise möchten Sie Ihrem Personendatensatz weitere Felder hinzufügen, daher ist die erste Option ungeeignet. Es ist auch sehr wahrscheinlich, dass Sie eine große Anzahl von Personen speichern müssen und Datensätze schnell nachschlagen möchten. Daher ist es auch keine gute Idee, sie in ein einfaches Array zu kopieren (wie in Option 2 beschrieben).

Das dritte Muster bietet Ihnen die Möglichkeit, eine beliebige Zeichenfolge als ID zu verwenden, komplexe Personenstrukturen zu haben und Personendatensätze in konstanter Zeit abzurufen und festzulegen. Es ist definitiv der richtige Weg.

Eine Sache, die Option 3 fehlt, ist eine stabile deterministische Reihenfolge (was der Vorteil von Option 2 ist). Wenn Sie dies benötigen, würde ich empfehlen, ein geordnetes Array von Personen-IDs als separate Struktur beizubehalten, wenn Sie Personen in der richtigen Reihenfolge auflisten müssen. Der Vorteil wäre, dass Sie mehrere solcher Arrays für unterschiedliche Ordnungen desselben Datensatzes behalten können.

Levik
quelle
0

Angesichts Ihrer Einschränkung, dass Sie immer nur den Namen als Wert haben, würde ich die erste Option auswählen. Es ist das sauberste, hat den geringsten Overhead und die schnellste Suche.

Nathaniel Reinhart
quelle