Wielowymiarowe tablice asocjacyjne w JavaScript

85

Istnieją następujące wyniki zapytania: (klucz1 i klucz2 mogą być dowolnym tekstem)

id   key1     key2     value

1    fred     apple    2
2    mary     orange   10
3    fred     banana   7
4    fred     orange   4
5    sarah    melon    5
...

i chcę przechowywać dane w siatce (może jako tablica) zapętlając wszystkie rekordy w ten sposób:

         apple    orange   banana  melon
fred        2        4         7     -
mary        -        10        -     -
sarah       -        -         -     5

W PHP byłoby to naprawdę łatwe, używając tablic asocjacyjnych:

$result['fred']['apple'] = 2;

Ale w tablicach asocjacyjnych JavaScript takich jak ta nie działa. Po przeczytaniu mnóstwa samouczka, wszystko, co mogłem uzyskać, to:

arr=[];
arr[1]['apple'] = 2;

ale arr['fred']['apple'] = 2;nie działa. Próbowałem tablic obiektów, ale właściwości obiektów nie mogą być dowolnym tekstem. Im więcej czytałem samouczki, tym bardziej byłem zdezorientowany ...

Każdy pomysł jest mile widziany :)

Omiod
źródło
Dzięki za odpowiedzi, ale przeglądam wyniki zapytania i chcę ustawić wartości pojedynczo. Przykładowe wiersze (wzięte z przykładu Matta) var grid = {};grid['aa']['bb'] = 1;zwracają „Uncaught TypeError: Cannot set property 'bb' undefined". Mogę się mylić, ale w przypadku większości twoich przykładów muszę znać dane w czasie inicjalizacji.
Omiod
Właśnie stwierdziłem, że var grid = {}; grid['aa'] = {}; grid['aa']['bb'] = 1;działa. Bardziej złożony test kończy się niepowodzeniem, ale wygląda na to, że jestem na dobrej drodze
Omiod
2
musisz najpierw zainicjować podobiekt, jak wspomniałem w mojej odpowiedzi. var grid = {}; grd ['aa'] = {}; wtedy możesz zrobić grid ['aa'] ['bb'] = 1. Istnieje wiele sposobów sprawdzenia, czy obiekt podrzędny jest już zainicjowany (jak wspomniałem w mojej odpowiedzi), więc nie nadpisujesz istniejącego obiekt.
Matt,
zaktualizowałem moją odpowiedź o dodatkowy kod. nie jestem pewien, jak głębokie są twoje obiekty lub jak pozyskujesz dane, ale mam nadzieję, że skieruje cię we właściwym kierunku
Matt

Odpowiedzi:

155

Po prostu użyj zwykłego obiektu JavaScript, który „czyta” w taki sam sposób jak tablice asocjacyjne. Musisz również pamiętać o ich inicjalizacji.

var obj = {};

obj['fred'] = {};
if('fred' in obj ){ } // can check for the presence of 'fred'
if(obj.fred) { } // also checks for presence of 'fred'
if(obj['fred']) { } // also checks for presence of 'fred'

// The following statements would all work
obj['fred']['apples'] = 1;
obj.fred.apples = 1;
obj['fred'].apples = 1;

// or build or initialize the structure outright
var obj = { fred: { apples: 1, oranges: 2 }, alice: { lemons: 1 } };

Jeśli przeglądasz wartości, możesz mieć coś, co wygląda tak:

var people = ['fred', 'alice'];
var fruit = ['apples', 'lemons'];

var grid = {};
for(var i = 0; i < people.length; i++){
    var name = people[i];
    if(name in grid == false){
        grid[name] = {}; // must initialize the sub-object, otherwise will get 'undefined' errors
    }

    for(var j = 0; j < fruit.length; j++){
        var fruitName = fruit[j];
        grid[name][fruitName] = 0;
    }
}
Matt
źródło
Udało się użyć przykładu w kodzie produkcyjnym (rozszerzenie Chrome) i działa dobrze. Dzięki. Wreszcie rozumiem, jak obsługiwać obiekt w JS!
Omiod
3
Nie mogę wystarczająco dobrze zagłosować na tę odpowiedź! Częścią, która powodowała, że ​​mój obiekt nieustannie zwracał undefined, było to, że NIE inicjowałem podobiektu. Więc pamiętaj, aby to zrobić! przykład: grid[name] = {};
Jason Pudzianowski
1
@Matt Czy istnieje sposób na pobranie obj.fred.apples przy użyciu tylko jednego zestawu []? Zakładam, że nie. obj["fred.apples"]na przykład
B3RV
A co, jeśli nie znamy liczby tablicy, na przykład twojego kodu [javasript] var people = ['fred', 'alice']; var owoce = ['jabłka', 'cytryny']; var color = ['red', 'blue'] var ..... -> liczba tablicy jest nieznana [/ javascript]
YukiSakura
Czy mogę uzyskać dostęp do pierwszego elementu w obj (fred) według indeksu liczbowego? Podobnie jak obj [0], co daje jabłka: 1, pomarańcze: 2. Czy mogę uzyskać dostęp do ostatniego przedmiotu (który jest alicja i owocuje cytrynami: 1)? ''
Timo
26

Jeśli nie musi to być tablica, możesz utworzyć „wielowymiarowy” obiekt JS ...

<script type="text/javascript">
var myObj = { 
    fred: { apples: 2, oranges: 4, bananas: 7, melons: 0 }, 
    mary: { apples: 0, oranges: 10, bananas: 0, melons: 0 }, 
    sarah: { apples: 0, oranges: 0, bananas: 0, melons: 5 } 
}

document.write( myObject[ 'fred' ][ 'apples' ] );
</script>
charliegriefer
źródło
JSON jest w rzeczywistości formatem „stringified” obiektu JavaScript. To, co tam masz, to nie JSON, tylko obiekt JavaScript.
Matt
Dzięki, Matt. Zaktualizowano post.
charliegriefer
1
@charliegriefer, nie powinno w wierszu document.write być: "document.write (myObj ......"
john
13

Javascript jest elastyczny:

var arr = {
  "fred": {"apple": 2, "orange": 4},
  "mary": {}
  //etc, etc
};

alert(arr.fred.orange);
alert(arr["fred"]["orange"]);
for (key in arr.fred)
    alert(key + ": " + arr.fred[key]);
cambraca
źródło
8
Powiedziałbym, że nazwa zmiennej „arr” jest tutaj błędna, ponieważ w rzeczywistości nie jest to tablica, ale obiekt.
Matt,
Jestem leniwym psem i dlatego nie mogę skorzystać z twojego rozwiązania. Gdybym był chętny, znalazłbym drogę z Twoim przykładem podtablicy;) W każdym razie dziękuję za twój wysiłek.
Timo
9

Ponieważ potrzebowałem uzyskać wszystkie elementy w przyjemny sposób, natknąłem się na temat SO „Przechodzenie przez 2-wymiarową tablicę asocjacyjną / obiekt” - bez względu na to, jak się nazywam, bo liczy się funkcjonalność.

var imgs_pl = { 
    'offer':        { 'img': 'wer-handwritter_03.png', 'left': 1, 'top': 2 },
    'portfolio':    { 'img': 'wer-handwritter_10.png', 'left': 1, 'top': 2 },
    'special':      { 'img': 'wer-handwritter_15.png', 'left': 1, 'top': 2 }
};
for (key in imgs_pl) { 
    console.log(key);
    for (subkey in imgs_pl[key]) { 
        console.log(imgs_pl[key][subkey]);
    }
}
Michał - wereda-net
źródło
Należy zaakceptować odpowiedź, ponieważ zwykle jest to pętla.
Timo
6

Wydaje się, że w przypadku niektórych aplikacji istnieje znacznie prostsze podejście do wielowymiarowych tablic asocjacyjnych w javascript.

  1. Biorąc pod uwagę, że wewnętrzna reprezentacja wszystkich tablic jest w rzeczywistości obiektami obiektów, wykazano, że czas dostępu do elementów indeksowanych numerycznie jest w rzeczywistości taki sam, jak w przypadku elementów indeksowanych asocjacyjnie (tekstowo).

  2. czas dostępu do indeksowanych asocjacyjnych elementów pierwszego poziomu nie rośnie wraz ze wzrostem liczby rzeczywistych elementów.

Biorąc to pod uwagę, może istnieć wiele przypadków, w których faktycznie lepiej jest użyć podejścia połączonego ciągu, aby stworzyć równoważność elementów wielowymiarowych. Na przykład:

store['fruit']['apples']['granny']['price] = 10
store['cereal']['natural']['oats']['quack'] = 20

idzie do:

store['fruit.apples.granny.price'] = 10
store['cereal.natural.oats.quack'] = 20

Zalety obejmują:

  • nie ma potrzeby inicjowania obiektów podrzędnych ani zastanawiania się, jak najlepiej łączyć obiekty
  • jednopoziomowy czas dostępu. obiekty w obiektach potrzebują N-krotności czasu dostępu
  • może użyć Object.keys (), aby wyodrębnić wszystkie informacje o wymiarach i ...
  • można użyć funkcji regex.test (string) i funkcji array.map na klawiszach, aby wyciągnąć dokładnie to, czego chcesz.
  • brak hierarchii w wymiarach.
  • „kropka” jest dowolna - użycie podkreślenia w rzeczywistości ułatwia wyrażanie regularne
  • istnieje wiele skryptów do „spłaszczania” JSON do i z tego formatu, jak również
  • może używać wszystkich innych fajnych funkcji przetwarzania tablic na liście kluczy
sdw
źródło
3

Uzyskaj wartość dla tablicy właściwości tablic asocjacyjnych, gdy nazwa właściwości jest liczbą całkowitą:

Rozpoczynając od tablicy asocjacyjnej, w której nazwy właściwości są liczbami całkowitymi:

var categories = [
    {"1":"Category 1"},
    {"2":"Category 2"},
    {"3":"Category 3"},
    {"4":"Category 4"}
];

Wypchnij elementy do tablicy:

categories.push({"2300": "Category 2300"});
categories.push({"2301": "Category 2301"});

Przejdź przez tablicę i zrób coś z wartością właściwości.

for (var i = 0; i < categories.length; i++) {
    for (var categoryid in categories[i]) {
        var category = categories[i][categoryid];
        // log progress to the console
        console.log(categoryid + " : " + category);
        //  ... do something
    }
}

Wynik konsoli powinien wyglądać następująco:

1 : Category 1
2 : Category 2
3 : Category 3
4 : Category 4
2300 : Category 2300
2301 : Category 2301

Jak widać, można obejść ograniczenie tablicy asocjacyjnej i mieć nazwę właściwości będącą liczbą całkowitą.

UWAGA: Tablica asocjacyjna w moim przykładzie to json, który miałbyś, gdybyś zserializował obiekt Dictionary [].

Jason Williams
źródło
3

Nie używaj tablicy, używaj obiektu.

var foo = new Object();
Tim
źródło
2
nie używaj a new Object(), ponieważ Object.prototype może mieć dołączoną dziwność; użyj dosłownego obiektu:var foo = {};
Michael Paulukonis
1
@Michael Myślałem, że {} to tylko cukier syntaktyczny dla nowego Object (); Podobnie jak [] jest skrótem od nowej Array ()
Matt
1
z mojej konsoli Chrome JS oba te skróty wyglądają identycznie, w tym ich prototypy. Nawet dodanie Object.prototype.foo = function () {} pojawia się w obu.
Matt
1
@Matt Myślę, że się mylę, pomyliłem to, new Array()czego należy unikać. ugh.
Michael Paulukonis
1
@Michael, dlaczego należy unikać nowej Array ()?
Matt,
2

Nie musisz koniecznie używać obiektów, możesz to zrobić z normalnymi wielowymiarowymi tablicami.

Oto moje rozwiązanie bez obiektów :

// Javascript
const matrix = [];

matrix.key1 = [
  'value1',
  'value2',
];

matrix.key2 = [
  'value3',
];

który w PHP jest odpowiednikiem:

// PHP
$matrix = [
    "key1" => [
        'value1',
        'value2',
    ],
    "key2" => [
        'value3',
    ]
];
Francesco Borzi
źródło
1
Jak później zapętlić (użyć każdego)?
Sagive SEO
0

<script language="javascript">

// Set values to variable
var sectionName = "TestSection";
var fileMap = "fileMapData";
var fileId = "foobar";
var fileValue= "foobar.png";
var fileId2 = "barfoo";
var fileValue2= "barfoo.jpg";

// Create top-level image object
var images = {};

// Create second-level object in images object with
// the name of sectionName value
images[sectionName] = {};

// Create a third level object
var fileMapObj = {};

// Add the third level object to the second level object
images[sectionName][fileMap] = fileMapObj;

// Add forth level associate array key and value data
images[sectionName][fileMap][fileId] = fileValue;
images[sectionName][fileMap][fileId2] = fileValue2;


// All variables
alert ("Example 1 Value: " + images[sectionName][fileMap][fileId]);

// All keys with dots
alert ("Example 2 Value: " + images.TestSection.fileMapData.foobar);

// Mixed with a different final key
alert ("Example 3 Value: " + images[sectionName]['fileMapData'][fileId2]);

// Mixed brackets and dots...
alert ("Example 4 Value: " + images[sectionName]['fileMapData'].barfoo);

// This will FAIL! variable names must be in brackets!
alert ("Example 5 Value: " + images[sectionName]['fileMapData'].fileId2);
// Produces: "Example 5 Value: undefined".

// This will NOT work either. Values must be quoted in brackets.
alert ("Example 6 Value: " + images[sectionName][fileMapData].barfoo);
// Throws and exception and stops execution with error: fileMapData is not defined

// We never get here because of the uncaught exception above...
alert ("The End!");
</script>

Rich Schramm
źródło
0
    var myObj = [];
    myObj['Base'] = [];
    myObj['Base']['Base.panel.panel_base'] = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',  AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };
    myObj['Base']['Base.panel.panel_top']  = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };

    myObj['SC1'] = [];
    myObj['SC1']['Base.panel.panel_base'] = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',  AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };
    myObj['SC1']['Base.panel.panel_top']  = {ContextParent:'',ClassParent:'',NameParent:'',Context:'Base',Class:'panel',Name:'panel_base',Visible:'',ValueIst:'',ValueSoll:'',
                                              Align:'',AlignFrom:'',AlignTo:'',Content:'',onClick:'',Style:'',content_ger_sie:'',content_ger_du:'',content_eng:'' };


    console.log(myObj);

    if ('Base' in myObj) {
      console.log('Base found');

      if ('Base.panel.panel_base' in myObj['Base'])  {
        console.log('Base.panel.panel_base found'); 


      console.log('old value: ' + myObj['Base']['Base.panel.panel_base'].Context);  
      myObj['Base']['Base.panel.panel_base'] = 'new Value';
      console.log('new value: ' + myObj['Base']['Base.panel.panel_base']);
      }
    }

Wynik:

  • Znaleziono bazę
  • Znaleziono Base.panel.panel_base
  • stara wartość: podstawa
  • nowa wartość: nowa wartość

Operacja tablicowa działa. Nie ma problemu.

Iteracja:

     Object.keys(myObj['Base']).forEach(function(key, index) {            
        var value = objcons['Base'][key];                   
      }, myObj);
Necips
źródło
2
Witamy w stackoverflow. Zmień swoją odpowiedź i wyjaśnij, dlaczego fragmenty kodu rozwiązują problem. Więcej informacji tutaj: Jak odpowiedzieć
Djensen