Obiekty a tablice w JavaScript dla par klucz / wartość

83

Załóżmy, że masz bardzo prostą strukturę danych:

(personId, name)

... i chcesz przechowywać wiele z nich w zmiennej javascript. Jak widzę, masz trzy opcje:

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

Druga lub trzecia opcja jest oczywiście dobrym rozwiązaniem, jeśli masz (lub spodziewasz się, że możesz mieć) więcej niż jedną „wartościową” część do przechowywania (np. Dodając wiek lub coś w tym rodzaju), więc dla dobra argumentacji, załóżmy, że nigdy, przenigdy nie będzie więcej potrzebnych wartości danych w tej strukturze. Który wybierasz i dlaczego?


Edycja : przykład pokazuje teraz najbardziej typową sytuację: niesekwencyjne identyfikatory.

nickf
źródło
1
Istnieje również tablica 2d: var people = [[1, 'Joe'],[3, 'Sam'],[8,'Eve']];(która nie daje szybkiego wyszukiwania według identyfikatora, ale jest kolejną opcją przechowywania danych)
Użytkownik

Odpowiedzi:

58

Każde rozwiązanie ma swoje przypadki użycia.

Myślę, że pierwsze rozwiązanie jest dobre, jeśli próbujesz zdefiniować relację jeden do jednego (na przykład proste mapowanie), zwłaszcza jeśli potrzebujesz użyć klucza jako klucza wyszukiwania.

Drugie rozwiązanie wydaje mi się ogólnie najsolidniejsze i prawdopodobnie użyłbym go, gdybym nie potrzebował klucza szybkiego wyszukiwania:

  • Jest samoopisujące, więc nie musisz polegać na nikim, kto używa ludzi, aby wiedzieć, że klucz jest identyfikatorem użytkownika.
  • Każdy obiekt jest samowystarczalny, co jest lepsze do przekazywania danych w innym miejscu - zamiast dwóch parametrów (id i nazwa) po prostu podajesz ludzi.
  • Jest to rzadki problem, ale czasami wartości kluczy mogą nie nadawać się do użycia jako klucze. Na przykład, kiedyś chciałem zmapować konwersje ciągów (np. „:” Na „>”), ale ponieważ „:” nie jest prawidłową nazwą zmiennej, musiałem użyć drugiej metody.
  • Jest łatwo rozszerzalny, na wypadek gdybyś gdzieś wzdłuż linii musiał dodać więcej danych do niektórych (lub wszystkich) użytkowników. (Przepraszam, wiem o twoim „dla dobra kłótni”, ale to jest ważny aspekt.)

Trzeci byłby dobry, gdybyś potrzebował szybkiego czasu wyszukiwania + niektóre z wymienionych powyżej zalet (przekazywanie danych, samoopisywanie). Jeśli jednak nie potrzebujesz szybkiego czasu wyszukiwania, jest to dużo bardziej kłopotliwe. Tak czy inaczej, ryzykujesz popełnienie błędu, jeśli identyfikator obiektu różni się w jakiś sposób od identyfikatora ludzi .

Dan Lew
źródło
1
w trzecim punkcie. Aby uniknąć tego problemu, dostęp do właściwości obiektów można uzyskać w notacji nawiasów: var x = {"ab> c ++": "foo"}; alert (x ['ab> c ++']);
nickf
Wydaje mi się, że masz problem z kilkoma zastrzeżonymi słowami z właściwościami. Ale nie ma problemów, gdy używasz liczb.
derobert
4
@derobert: Nie sądzę. x = {"usuń": 1, "for": 2, "funkcja": 3}; - jest to ważne i można uzyskać do niego dostęp w ten sam sposób.
nickf
7

Właściwie istnieje czwarta opcja:

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

ponieważ twoje wartości następują po sobie. (Oczywiście będziesz musiał dodać / odjąć jeden - lub po prostu wstawić undefined jako pierwszy element).

Osobiście poszedłbym z twoim (1) lub (3), ponieważ te będą najszybsze do wyszukania kogoś po ID (O log n w najgorszym). Jeśli musisz znaleźć id 3 w (2), możesz albo wyszukać go według indeksu (w takim przypadku my (4) jest w porządku) lub musisz wyszukać - O (n).

Wyjaśnienie: mówię, że O (log n ) jest najgorszym, jakie może być, ponieważ AFAIK i implementacja mogą zdecydować o użyciu zrównoważonego drzewa zamiast tabeli skrótów. Tablicą skrótów byłaby O (1), zakładając minimalne kolizje.

Edycja z nickf: od tego czasu zmieniłem przykład w OP, więc ta odpowiedź może nie mieć już tak dużego sensu. Przeprosiny.

Opublikował

Ok, po edycji, wybrałbym opcję (3). Jest rozszerzalny (łatwe do dodawania nowych atrybutów), oferuje szybkie wyszukiwanie i może być również iterowany. Pozwala także w razie potrzeby przejść z powrotem do dokumentu tożsamości.

Opcja (1) byłaby przydatna, jeśli (a) potrzebujesz oszczędzać pamięć; (b) nigdy nie musisz wracać od obiektu z powrotem do identyfikatora; (c) nigdy nie rozszerzysz przechowywanych danych (np. nie możesz dodać nazwiska osoby)

Opcja (2) jest dobra, jeśli (a) musisz zachować zamówienie; (b) potrzeba iteracji wszystkich elementów; (c) nie musisz wyszukiwać elementów według id, chyba że jest posortowane według id (możesz przeprowadzić wyszukiwanie binarne w O (log n ). Uwaga, oczywiście, jeśli chcesz to posortować, zapłacisz koszt na wkładce.

derobert
źródło
Rozumiem, dlaczego (1) i (3) są szybsze niż (2), ale zastanawiam się, skąd masz O (log (n)) na czas? Czy nie powinno to być O (1), ponieważ identyfikatory powinny być przechowywane w tablicy mieszania?
Nathaniel Reinhart
Podałem log n jako najgorszy, ponieważ widziałem implementację decydującą się na użycie zrównoważonego drzewa zamiast tablicy mieszania. Tablicą skrótów byłaby oczywiście O (1) [zakładając, że kolizje są minimalne].
derobert
identyfikatory nie zawsze są tak proste i rzadko byłyby to kolejne IRL, dlatego musisz je określić. Przyznaję, że był to jednak słaby / niejednoznaczny przykład.
nickf
+1, twoja odpowiedź nie jest gorsza niż zaakceptowana ... ale twoje czwarte rozwiązanie nie przechowuje wszystkich danych, więc oto prawidłowa czwarta opcja: var people = []; people[1] = 'Joe'; people[3] = 'Sam'; people[8] = 'Eve';lub jako dwuwymiarowa tablica: var people = []; people[1] = ['Joe', 'Smith']; people[3] = ['Sam', 'Miller']; people[8] = ['Eve', 'Sweet'];(będzie dostępna np. 'People [ 3] [1]; 'co bardzo dobrze sprawdza się w przypadku szybkiego wewnętrznego przechowywania danych w dowolnej klasie - cóż, z klasą mam na myśli "nową Wh EverythingFunction ();" rzecz, której javascripters nie lubi; -) ...
Marcus
... Ale to działa tylko wtedy, gdy id jest liczbą całkowitą, w przeciwnym razie "klasyczne" rozwiązanie 2 (razem ze słynnym for(var key in object)z dostępu) jest drogą do zrobienia w normalnych przypadkach (ma O (n / 2)) lub rozwiązanie 3 jeśli masz naprawdę dużą tablicę do szybszego dostępu, ponieważ ma O (1) ... i na koniec zapomnijmy o powyższym rozwiązaniu 1, nie tylko jeśli twój identyfikator jest liczbą całkowitą, ale także w przypadku, gdy identyfikator nie jest liczbą całkowitą: var people = {'Smith':'Joe', 'Miller':'Sam', 'Sweet': 'Eve' };( ponieważ musiałbyś uzyskać dostęp do swojego obiektu w ten sposób: alert(people.Smith);co nie jest zbyt miłe!).
Marcus
2

Zakładając, że dane nigdy się nie zmienią, pierwsza opcja (pojedynczy obiekt) jest najlepsza.

Prostota struktury oznacza, że ​​jest ona najszybsza do przeanalizowania, aw przypadku małych, rzadko (lub nigdy) zmieniających się zestawów danych, takich jak ten, mogę sobie tylko wyobrazić, że będzie on często wykonywany - w takim przypadku minimalne obciążenie jest tak trzymać.

karim79
źródło
2

Stworzyłem małą bibliotekę do zarządzania parami klucz-wartość.

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

To używa

  • obiekt do przechowywania kluczy, który pozwala na szybkie usuwanie i odzyskiwanie wartości oraz
  • lista połączona, aby umożliwić naprawdę szybką iterację wartości

Mam nadzieję, że to pomoże :)

scaraveos
źródło
dlaczego nie dodać linku jsperf?
Janus Troelsen
ta biblioteka jest niesamowita, świetna robota. Zaoszczędził mi tylko trochę czasu.
Chris Hough
1

Trzecia opcja jest najlepsza dla każdej przyszłościowej aplikacji. Prawdopodobnie będziesz chciał dodać więcej pól do swojego rekordu osoby, więc pierwsza opcja jest nieodpowiednia. Jest również bardzo prawdopodobne, że będziesz mieć dużą liczbę osób do przechowywania i będziesz chciał szybko wyszukać rekordy - w ten sposób zrzucenie ich do prostej tablicy (tak jak jest to zrobione w opcji # 2) również nie jest dobrym pomysłem.

Trzeci wzorzec daje możliwość użycia dowolnego łańcucha jako identyfikatora, ma złożone struktury Person oraz pobiera i ustawia rekordy osób w stałym czasie. To zdecydowanie najlepsza droga.

Jedyną rzeczą, której brakuje opcji nr 3, jest stabilna deterministyczna kolejność (co jest zaletą opcji nr 2). Jeśli tego potrzebujesz, radziłbym zachować uporządkowaną tablicę identyfikatorów osób jako osobną strukturę na wypadek, gdybyś musiał uporządkować listę osób. Zaletą byłoby to, że można przechowywać wiele takich tablic dla różnych kolejności tego samego zestawu danych.

Levik
źródło
0

Biorąc pod uwagę twoje ograniczenie, że zawsze będziesz mieć tylko nazwę jako wartość, wybrałbym pierwszą opcję. Jest najczystszy, ma najmniejszy narzut i najszybszy podgląd.

Nathaniel Reinhart
źródło