Багатовимірні асоціативні масиви в JavaScript


84

Існують такі результати запиту: (key1 і key2 можуть бути будь-яким текстом)

id   key1     key2     value

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

і я хочу зберегти дані в сітці (можливо, як масив), циклічно записуючи всі записи таким чином:

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

У PHP це було б дуже просто, використовуючи асоціативні масиви:

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

Але в JavaScript такі асоціативні масиви не працюють. Прочитавши безліч підручників, я міг отримати лише таке:

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

але arr['fred']['apple'] = 2;не працює. Я спробував масиви об’єктів, але властивості об’єктів не можуть бути вільним текстом. Чим більше я читав підручники, тим більше заплутувався ...

Будь-яка ідея вітається :)


Дякую за відповіді, але я переглядаю результати запиту, і я хочу встановити значення по одному. Приклади рядків (взяті з прикладу Matt) var grid = {};grid['aa']['bb'] = 1;повертають "Uncaught TypeError: Неможливо встановити властивість 'bb' з undefined". Я можу помилитися, але з більшістю ваших прикладів я повинен знати дані під час ініціалізації.
Omiod

Щойно знайшов, що var grid = {}; grid['aa'] = {}; grid['aa']['bb'] = 1;працює. Більш складний тест не вдається, але, схоже, я на правильному шляху
Оміод,

2
вам слід спочатку ініціалізувати під-об’єкт, як я вже згадував у своїй відповіді. var сітка = {}; grd ['aa'] = {}; тоді ви можете зробити grid ['aa'] ['bb'] = 1. Існує багато способів перевірити, чи не ініціалізований суб-об'єкт (як зазначено у моїй відповіді), тому ви не переписуєте існуючий об'єкт.
Метт

оновив мою відповідь додатковим кодом. не впевнений, наскільки глибокі ваші об’єкти або як ви отримуєте свої дані, але, сподіваємось, вкаже вас у правильному напрямку
Мет

Відповіді:


152

Просто використовуйте звичайний об'єкт JavaScript, який буде "читати" так само, як і ваші асоціативні масиви. Ви також повинні пам’ятати про їх ініціалізацію.

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

Якщо ви переглядаєте значення, у вас може бути щось подібне до цього:

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

Вдалося використати приклад у виробничому коді (розширення Chrome) і чудово працює. Дякую. Нарешті, я отримую, як обробляти об’єкт у JS!
Оміод,

3
Не можу достатньо проголосувати за цю відповідь! Частина, з-за якої мій об’єкт постійно повертався невизначеним, полягала в тому, що я НЕ ініціалізував під-об’єкт. Тож обов’язково зробіть це! приклад: grid[name] = {};
Джейсон Пудзяновський

1
@Matt Чи є спосіб отримати obj.fred.apples, використовуючи лише один набір []? Я припускаю, що ні. obj["fred.apples"]наприклад
theB3RV

Що робити, якщо ми не знаємо кількість масиву, наприклад ваш код [javasript] var people = ['fred', 'alice']; var fruit = ['яблука', 'лимони']; var color = ['red', 'blue'] var ..... -> кількість масиву невідома [/ javascript]
YukiSakura

Чи можу я отримати доступ до першого елемента в obj (fred) за числовим індексом? Як obj [0], результатом якого є яблука: 1, апельсини: 2. Чи можу я отримати доступ до останнього предмета (аліси і результатів для лимонів: 1)? `
Тимо,

26

Якщо це не повинен бути масив, ви можете створити "багатовимірний" об'єкт 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>

JSON насправді є "розрядженим" форматом об'єкта JavaScript. Те, що у вас є, - це не JSON, а лише об’єкт JavaScript.
Метт

Дякую, Метте. Оновив пост.
charliegriefer

1
@charliegriefer, чи не повинен рядок document.write бути: "document.write (myObj ......"
Джон

13

Javascript є гнучким:

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]);

8
Я б сказав, що назва змінної 'arr' тут є неправильним, оскільки насправді це не масив, а об'єкт.
Matt

Я ледача собака і тому не можу використовувати ваше рішення. Якби я прагнув, я знайшов би шлях із вашим прикладом підмасивів;) У будь-якому разі, дякую за ваші зусилля.
Тимо

9

Оскільки мені потрібно було отримати всі елементи в хороший спосіб, я зіткнувся з цією темою SO "Обхід двовимірного асоціативного масиву / об'єкта" - незалежно від назви для мене, оскільки функціональність має значення.

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]);
    }
}

Слід прийняти відповідь, оскільки петля, як правило, шлях.
Тимо

6

Здається, що для деяких додатків існує набагато простіший підхід до багатовимірних асоціативних масивів у javascript.

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

  2. час доступу до асоційованих індексованих елементів першого рівня не збільшується, оскільки збільшується кількість фактичних елементів.

Враховуючи це, може бути багато випадків, коли насправді краще використовувати зв’язаний рядковий підхід для створення еквівалентності багатовимірних елементів. Наприклад:

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

йде на:

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

Серед переваг:

  • немає необхідності ініціалізувати під-об'єкти або з'ясувати, як найкраще поєднувати об'єкти
  • однорівневий час доступу. об'єкти всередині об'єктів потребують N разів часу доступу
  • може використовувати Object.keys () для вилучення всієї інформації про розміри та ..
  • може використовувати функцію regex.test (рядок) та функцію array.map на клавішах, щоб витягти саме те, що ви хочете.
  • відсутність ієрархії у вимірах.
  • "крапка" довільна - використання підкреслення насправді полегшує регулярні вирази
  • Є багато сценаріїв для "вирівнювання" JSON як у цей формат, так і поза ним
  • може використовувати всі інші приємні функції обробки масивів у списку ключів

3

Отримати значення масиву властивості асоціативних масивів, коли ім’ям властивості є ціле число:

Починаючи з асоціативного масиву, де імена властивостей є цілими числами:

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

Пересунути елементи до масиву:

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

Прокрутіть масив і зробіть щось із значенням властивості.

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
    }
}

Висновок консолі повинен виглядати так:

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

Як бачите, ви можете обійти обмеження асоціативного масиву і мати назву властивості як ціле число.

ПРИМІТКА: Асоціативний масив у моєму прикладі - це json, який ви мали б, якщо б ви серіалізували об’єкт Dictionary [].


3

Не використовуйте масив, використовуйте об'єкт.

var foo = new Object();

2
не використовуйте a new Object(), оскільки до Object.prototype може бути прив'язана дивина; використовувати буквальний об’єкт:var foo = {};
Michael Paulukonis

1
@Michael Я думав, що {} - це просто синтаксичний цукор для нового Object (); Подібно до [] - це скорочення від new Array ()
Мет

1
з моєї консолі Chrome JS обидва ці вбудови виглядають ідентично, включаючи їх прототипи. Навіть додавання Object.prototype.foo = function () {} відображається в обох.
Matt

1
@Matt Я думаю, що я помиляюся, я змішував те, new Array()чого слід уникати. тьфу.
Michael Paulukonis

1
@Michael, чому слід уникати нового Array ()?
Matt

2

Вам не потрібно обов'язково використовувати Об'єкти, це можна зробити за допомогою звичайних багатовимірних масивів.

Це моє рішення без об’єктів :

// Javascript
const matrix = [];

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

matrix.key2 = [
  'value3',
];

що в PHP еквівалентно:

// PHP
$matrix = [
    "key1" => [
        'value1',
        'value2',
    ],
    "key2" => [
        'value3',
    ]
];

1
Як ви цикл (використовувати кожен) це пізніше?
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>


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']);
      }
    }

Вихід:

  • Базу знайдено
  • Знайдено Base.panel.panel_base
  • старе значення: Base
  • нове значення: нове значення

Операція масиву працює. Немає ніяких проблем.

Ітерація:

     Object.keys(myObj['Base']).forEach(function(key, index) {            
        var value = objcons['Base'][key];                   
      }, myObj);

2
Ласкаво просимо до stackoverflow. Будь ласка, відредагуйте свою відповідь та поясніть, чому фрагменти коду вирішують проблему. Більше інформації тут: Як відповісти
Дженсен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.