[JS] dla danych - tablica asocjacyjna czy indeksowana?


(Farkas) #1

Załóżmy, że mamy klasę "car". Tworzymy obiekt podpinając go do cars[0]. Mamy także klasę "engine", a obiekty tej klasy są obiektami należącymi do obiektów klasy "car".

 

Można stworzyć tablice obiektów i będzie to wtedy wyglądać tak:

app.cars[0].engines[0]

Wtedy dość wygodnie będzie się iterować po tych obiektach, jako elementach tablicy, a elementy tablic będą wliczane we właściwość "length". 

 

Może jednak to wyglądać czytelniej dla człowieka, poprzez np. tworzenie obiektów klasy cars oraz klasy engines i przypięcie ich nie do cars[0] i engines[0], a np. cars["Citroen"] oraz engines["2.0"].

app.cars["Citroen"].engines["2.0"]

No i w ten sposób bez żadnego wyszukiwania można odwołać się po nazwie do konkretnego auta oraz jego silnika, metodą brzmiącą mniej więcej tak:

app.getEngine("2.0", "Citroen")

gdzie pierwszy parametr to silnik, a drugi to (tu z grubsza) nazwa samochodu. W przypadku funkcji podanej w pierwszym kawałku kodu wyglądałoby to mało atrakcyjnie, bo tak:

app.getEngine(0,0)

Obydwa rozwiązania można ze sobą pogodzić, np. poprzez stworzenie referencji, mniej więcej tak:

app.cars["Citroen"].engines["2.0"] = app.cars[0].engines[0]

Dość proste, oszczędzające pamięć rozwiązanie. Tutaj jednak zaśmiecamy tablice obiektów, bo nie dość, że mamy element 0 w tablicy "cars", to mamy też element "Citroen", który przechowuje, jak dobrze rozumiem - referencje. Jednak wartość "length" uwzględnia tylko elementy posiadające indeks, więc jeżeli chodzi o length - problemu nie ma.

 

A może stworzyć osobną tablicę z referencjami np. carsNames? Oprócz tych pytań wyżej, jak i w tytule tematu, mam jeszcze jedno istotne dla mnie pytanie: czy nie jest to temat poświęcony na pierdołę i może mogę zrobić jak mi się podoba, a nie będzie to robić wielkiej różnicy w przyszłości?

 

EDIT: Oczywiście nazwa auta dla danego elementu tablicy np. cars[0] zawierałaby się także jako właściwość obiektu tablicy, czyli np. cars[0].name == "Citroen".


(kostek135) #2

Javascript nie ma tablic asocjacyjnych.


(Farkas) #3

Nie chodzi mistrzu o wygodę. Co do braku tablic asocjacyjnych w JSie… dobrze, że edytowałeś post, bo jak sam widzisz - JS ma takowe. W pytaniu chodzi mi o wydajność, późniejsze wykorzystywanie takiej tablicy na różne sposoby, wady i zalety konkretnego typu tablicy czyli asocjacyjnej lub indeksowanej. W sumie istotniejsza dla mnie jest odpowiedź na pytanie ostatnie - czy nie pytam w temacie o mało istotną pierdołę? No ale na poprzednie odpowiedź jest mile widziana mimo wszystko :slight_smile:

 

Podkreślam, że jestem takim trochę amatorem :wink: Przykład wydajnościowy:

 

Załóżmy, że użytkownik zna tablicę aut tzn. listę aut czy tam firm z jej dokładnymi nazwami. Chce dostać informacje na temat jakiegoś konkretnego auta, którego nazwę doskonale zna. W szukajce, w aplikacji więc wpisuje: “Citroen”. Teraz aplikacja zamiast przeszukiwać całą tablicę w poszukiwaniu auta o nazwie Citroen, czyli każdego car[X].name, może po prostu odwołać się bezpośrednio do car[“Citroen”] i nic nie musi szukać. Wydaje się więc, że drugi z tych sposobów powinien być zdecydowanie szybszy.

 

Czy może inaczej to wygląda niż mi się tu wyżej wydaje i ja czegoś nie dostrzegam? Jak pisałem, dopiero rozpoczynam “ostrzejszą” praktykę w programowaniu pełnoprawnych aplikacji.


(kostek135) #4

 

Nie ma.

To co napisałem jest równoważne syntaktycznie:

 

<body>
<script>
var person = {};
person.FirstName = "John";
person.LastName = "Doe";

for (var x in person)
    console.log("propertyName = " + x + ", value = " + person[x]);
</script>
</body>

skałdnia nawiasów z kluczem napisowym została wprowadzona by można było dynamicznie odwoływać się do pola po nazwie. Javascript ma tylko obiekty i tablice indeksowane. Popatrz ze person w obu przypadkach deklaruje jako obiekt a nie tablicę.

 

Odpowiadanie na pytanie, której przesłanką jest nieprawda mija się z celem, ponieważ wynik nic nam nie powie z fałszu wynika zawsze prawda.

EDIT

Dokształć się trochę i wróć jak już będziesz coś wiedział: http://www.w3schools.com/js/js_arrays.asp


(Farkas) #5

w3schools jest niepolecanym źródłem wiedzy przez wiele osób. Po drugie - tak, wiem, że tablice asocjacyjne w JS to tylko prowizorka, ale jednak prowizorka podstawowo spełniająca tę funkcje. Po trzecie - dość dobrze się dokształciłem na ten temat już dawno temu i jeżeli napiszesz mi, że funkcja (też) jest obiektem, to wcale się nie zdziwię. Opierajmy się jednak na tym, co jest przyjęte. Klas w JSie niby też nie ma, ale z powodzeniem można w podstawowy sposób zastąpić je prototypami, jeżeli tylko zaakceptujemy fakt braku hermetyzacji. Hermetyzacje można sobie “zasymulować” poprzez choćby domknięcia, rezygnując z prototypów, ale tak, też wiem - nie jest to tym samym. Przyjmuje się mimo to, że tak można sobie zrealizować względnie pełnoprawne klasy. Wiedzę teoretyczną mam niemałą, mało mam praktyki. Póki co, średnio pomogłeś mi tym postem :stuck_out_tongue_winking_eye:

 

EDIT: Można zadeklarować tablice (pseudo)asocjacyjną np. 

person = []

Wcale nie musi to być deklaracja obiektu:

person = {}

Pierwsze działać też będzie.

 

EDIT2: “instanceof Array” dla tablicy asocjacyjnej daje wynik “true”. Natomiast jeżeli już zadeklarujemy jako obiekt, zamiast tablicę, to już jako wynik dostaniemy “false” :wink: Nieco pochopnie uznałem Ci rację, mając świadomość różnych “niby dziwności” JSa.


(Frankfurterium) #6

 

 

Ale tylko dlatego, że tablica też zachowuje się jak najzwyklejszy obiekt.

Prosty przykład

 

Kiedy wpychasz obiekty w tę twoją ‘tablicę asocjacyjną’, nie znajdują się one z resztą grzecznie poindeksowanych elementów, tylko leżą obok jako zwykłe pola w zwykłym obiekcie. Metody tablicy nie będą ich widziały. Nawet wielkość tablica zwróci niepoprawną, bo policzy tylko elementy indeksowane numerami.


(Farkas) #7

Zgadza się. Metody przeznaczone tablicom przestają działać, ale tylko na tych konkretnych polach. Jako pośredni przykład może posłużyć pierwszy wpis tematu, gdzie pisałem o “length”. Mimo wszystko - jest to tablica, można ją uznać za asocjacyjną. Działa tak samo, a to że metody tablicy jej nie obsługują, to trochę inna sprawa. Można stworzyć tablicę, utworzyć w niej same elementy, gdzie klucze są nazwane i tablica wcale nie przemianuje się w standardowy obiekt jak to pisze na w3schools. Dalej będzie tablicą, mimo że są w niej jedynie klucze nazwane, a nie liczbowe.

 

EDIT: Eh, dalej nie mam odpowiedzi na pytania w temacie… 


(Tomasz Gawron) #8

Najważniejsze jest żeby było wygodnie (jak pisał kolega wyzej). Jeśli np tablica indeksowane będzie ciut wydajniejsza ale będzie się wiązać z tym pisanie dodatkowych 100 linii kodu to gdzie tu sens jest? W zależności od problemu powinieneś sam dobrać odpowiednie rozwiązanie. Możesz sobie przygotować dwa rozwiązania i przetestować pod względem wydajności na jsperf.com (chwilowo niedotepna). Wybranie lepszej wydajnosciowo tablicy do tego problemu wcale nie będzie oznaczać ze jest to zrobione dobrze.


(Farkas) #9

Zdaję sobie sprawę, że wydajność nie jest jedynym wyznacznikiem. Dlatego też chciałem odpowiedzi kogoś doświadczonego. A ostatnie pytanie jest efektem tego, że chyba lepiej próbować, trochę zaryzykować niż pytać. Tym bardziej o pierdołę. Przemyślałem sobie to nieco (wybór tablicy) i doszedłem do własnych wniosków. Ogólną (a za razem dość konkretną) odpowiedzią na to pytanie jest “musisz dobrać odpowiednie rozwiązanie do problemu”, którą nadmieniasz i jest to chyba dobra wskazówka. W każdym razie dzięki za niezły post, adekwatny do jednego z pytań! :slight_smile:


(GioWDS) #10

Bo założenie masz błędne - [] to nic innego jak obiekt typu Array.

var x = [];
x["wartosc"] = 'a';
x.forEach(function(i,v){console.log(i,v)});

Jaki da Ci wynik?


(mikolaj_s) #11

 

Chyba już napisano to wyraźnie. Nie korzystasz z tablic asocjacyjnych choć wygląda to podobnie. Zastanów się czy dany car ma wiele enginów? Dla mnie raczej sensowniejsze i naturalne wydaje się aby był obiekt Car zawierający pola siedzący w talicy. Bez sensu jest tworzyć obiekty dla każego przypadku, to raczje kłóci się z pojęciem obiektu. 

W tym odzie miałbyś app.cars.Citroen.engine.2.0 czyli sześć poziomów obiektów, a same obiekty nie miałyby pól. Przyznasz, że to trochę dziwne. 

 

No czyba, że skorzystasz z ECMAScript 6 tam już jest kontener Map. 


(Farkas) #12

@GioWDS  wynik to pustka, nic, tak jak w przypadku innych metod. Dopiero po nadaniu indeksu elementowi coś tam się pokazuje, jak przy innych metodach. Natomiast jeżeli chodzi o to, że [] to Array i koniec, kropka, jak wyżej sam w jakiś sposób udowodniłem. Czasem określam tablicę z kluczami nazwanymi jako “pseudoasocjacyjna”, bo jak wyżej powiedziano - być może/prawdopodobnie nie można ją za takową w pełni uznać.

 

@mikolaj_s  troszkę dziwny wniosek z tymi poziomami, ale to być może dlatego, że nieprecyzyjnie ja się wyraziłem. Tym bardziej że nie jestem jakimś znawcą motoryzacji, a taki przykład podałem :stuck_out_tongue_winking_eye: Citroen to marka, nie konkretne auto, do którego przypisane są różne silniki. No i są różne marki i różne dla nich silniki. Każdy silnik to obiekt, który zawiera różne informacje o sobie. Podobnie jak Citroen zawierający oprócz silników inne informacje, jak nie wiem - rok założenia marki, pierwszy model. Można też uznać, że Citroen to konkretny model auta, ale wypuszczony w różnych wersjach, z różnymi silnikami. Jeżeli uznajesz, że powyższy fragment kodu źle jest do tego “dopasowany”, to zaproponuj lepszy - ja nie mam praktyki i chętnie się dowiem czy zobaczę konkretnie, co o tym myślisz. Szczerze mówiąc, trzymam się dość kurczowo definicji słownikowej tablicy asocjacyjnej. Z takową tablica z kluczami nazwanymi jest zgodna. Nie mam też specjalnego doświadczenia w programowaniu obiektowym. Wiem, że to dziwne, zabierać się najpierw za (specyficzną?) obiektówkę w JS, ale pozostańmy przy tym. Rozumiem teoretycznie na czym opiera się obiektówka w JS, poświęciłem także trochę czasu na to, aby to wszystko zrozumieć (this, prototypy, domknięcia, właściwości pseudoprywatne itd. itp.) i wolę przy tym pozostać. Tym bardziej że JS ogółem mi pasuje z różnych powodów.


(slepcu) #13

jeśli miałbym szukać po takiej tablicy, to wolałbym podawać parametry w formie:

var car = app.getCar({brand: "Citroen", engine: "2.0"});

możesz to potem zmienić nawet do formy:

var car = app.getCar({brand: ["Citroen","Renault"], engine: {gt: 2.0}});

czyli ze szukać aut marek Citroen i Renault o pojemności silnika większej niż 2.0 litra;

 

 

jako samą strukturę użyłbym zwyklych tablic, wraz z drugą tablicą dzialającą na zasadzie hashtabla:

var cars = [];
var cars.hashtable = {};

function add(newCar){
// newCar = {brand: "Renault", engine:2.0};
var idx = cars.push(newCar);
var hashName = getHashName(newCar);

cars.hashtable[hashName] = idx;
}
function search(cond){
// 
var hashName = getHashName(cond);
var idx = cars.hashtable.indexOf(hashname);
return cars[idx];
}

(Farkas) #14

Niezły, choć prosty kawałek kodu. Dobrze jest sobie móc porównać Twój do swojego i na szczęście też, nie widzieć jakichś wielkich różnic. Nieźle rozumiem JS, ale niezbyt angażowałem się w poznawanie metod i tym samym, kompletnie zapomniałem o indexOf, a Ty mi o nim przypomniałeś! :wink: W sumie to tak pamiętam o metodach, że przeszukiwanie zrealizowałbym prawdopodobnie zwykłą pętlą ostatecznie  :P W sumie to chyba dlatego, że do tej pory “siedzę” na tworzeniu obiektów, klas itp. 


(mikolaj_s) #15

Jak grochem o ścianę ;)  Cały czas wcześniejszy forumowicze i ja próbuję Ci to wyjaśnić. To wygląda jak tablica z kluczami, a naprawdę jest tworzeniem obiektów będących polami innego obiektu. Takie tablice pojawią są już w nowym standardzie jako Map. Jak piszesz tylko dla nowych przeglądarek to możesz spróbować wykorzystać.

 

@sleepcu podał Ci ładny przykład. Dodam tylko, że problem wygląda bardzo typowo. Takie dane przechowuje się w bazach relacyjnych, gdzie cars jest tablicą rekordów. W Twoim przypadku w kodzie Cars to obiekt z polami, a cars to tablica. W celu szybszego wyszukiwania musisz zapewnić istnienie  tablic z posortowanymi danymi lub metody indeksowania.

Notabene @sleepcu chyba pisał z palca i nie sprawdził czy ten kod w ogóle działa :wink: Nie zadziała druga linia, bo trzeba usunąć var.

Tworzy obiekt  hashtable, który też nie jest tablicą, a obiektem zwierającym w sobie pola o nazwie takim jak wyliczyła funkcja haszująca. Nie zadziała jednak indexof bo przecież nie jest to tablica. ale zadziała var idx = cars.hashtable[hashname];


(Farkas) #16

ale żeś się uczepił tej tablicy pseudoasocjacyjnej i jakoś nie widzę, aby inni uczepili się podobnie :stuck_out_tongue: Oszczędzę trochę dalszych dyskusji na ten temat: nieistotne jest dla mnie czy będzie to (faktycznie) obiekt przechowujący inne obiekty, czy tablica pseudoasocjacyjna z kluczami nazwanymi przechowująca jako wartości inne obiekty. Celowo dlatego też nazywam ją pseudoasocjacyjną. Na upartego można ująć, że zwykła indeksowana tablica przechowująca stringi, tak naprawdę przechowuje obiekty… typu string. Jednak plus dla Ciebie za pozostałą część posta, bo widzę przydatne informacje, jak sposoby na przyspieszenie wyszukiwania czy już wcześniej też w Twoich postach - informacja o “mapach”. Na ten moment zależy mi na kompatybilności i klasycznym podejściu tzn. prototypy i brak właściwości prywatnych, bo są mi w sumie zbędne, ale będzie na przyszłość :slight_smile:


(slepcu) #17

@mikolaj_s :

jo, zapomniałem o tym var, miałem usunąć. nie ma też np. definicji getHashName; 


(mikolaj_s) #18

“Celowo dlatego też nazywam ją pseudoasocjacyjną”

 

Ale wtedy musisz pamiętać o tym, że nie ma metod takich jak tablica. Po prostu moim zdaniem lepiej nazywać, rzeczy po imieniu, bo się wtedy łatwiej nie pogubić. :wink: