Многомерные ассоциативные массивы в JavaScript

85

Получены следующие результаты запроса: (ключ1 и ключ2 могут быть любым текстом)

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;не работает. Я пробовал массивы объектов, но свойства объектов не могут быть свободным текстом. Чем больше я читал уроки, тем больше запутывался ...

Любая идея приветствуется :)

Омиод
источник
Спасибо за ответы, но я просматриваю результаты запроса в цикле и хочу устанавливать значения по одному. Строки примера (взятые из примера Мэтта) var grid = {};grid['aa']['bb'] = 1;возвращают «Uncaught TypeError: Cannot set property 'bb' of undefined». Я могу ошибаться, но с большинством ваших примеров мне нужно знать данные во время инициализации.
Omiod 01
Только что обнаружил, что это var grid = {}; grid['aa'] = {}; grid['aa']['bb'] = 1;работает. Более сложный тест не
прошел
2
вы должны сначала инициализировать подобъект, как я уже упоминал в своем ответе. var grid = {}; grd ['aa'] = {}; тогда вы можете сделать grid ['aa'] ['bb'] = 1. Есть много способов проверить, инициализирован ли подобъект уже (как упоминалось в моем ответе), поэтому вы не перезаписываете существующий объект.
Мэтт
обновил свой ответ дополнительным кодом. не уверен, насколько глубоки ваши объекты или как вы получаете свои данные, но, надеюсь, укажет вам правильное направление
Мэтт

Ответы:

155

Просто используйте обычный объект 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!
Omiod 01
3
Не могу проголосовать за этот ответ достаточно! Часть, которая заставляла мой объект постоянно возвращать undefined, заключалась в том, что я НЕ инициализировал подобъект. Так что обязательно сделайте это! Пример: 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 01
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 здесь неправильно, потому что на самом деле это не массив, а объект.
Мэтт
Я ленивая собака и поэтому не могу воспользоваться вашим решением. Если бы я был нетерпелив, я бы нашел свой путь с вашим примером подмассива;) В любом случае, спасибо за ваши усилия.
Timo
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]);
    }
}
Михал - wereda-net
источник
Должен быть принят ответ, потому что цикл - это обычно путь.
Timo
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 (string) и функцию array.map для клавиш, чтобы получить именно то, что вы хотите.
  • нет иерархии в измерениях.
  • "точка" произвольная - использование подчеркивания упрощает регулярное выражение
  • есть много скриптов для "сглаживания" JSON в этот формат и из него.
  • может использовать все другие функции обработки массива в списке ключей
SDW
источник
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 = {};
Майкл Паулуконис 01
1
@Michael Я думал, что {} просто синтаксический сахар для new Object (); Во многом [] - это сокращение от new Array ()
Мэтт
1
из моей консоли Chrome JS обе эти команды кажутся идентичными, включая их прототипы. Даже добавление Object.prototype.foo = function () {} появляется в обоих.
Мэтт
1
@Matt Я думаю, что ошибаюсь, я смешивал то, с new Array()чем следует избегать. тьфу.
Майкл Паулюконис 01
1
@Michael, почему следует избегать new Array ()?
Мэтт
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 найдена
  • старое значение: База
  • новое значение: новое значение

Операция с массивом работает. Нет проблем.

Итерация:

     Object.keys(myObj['Base']).forEach(function(key, index) {            
        var value = objcons['Base'][key];                   
      }, myObj);
Necips
источник
2
Добро пожаловать в stackoverflow. Измените свой ответ и объясните, почему фрагменты кода решают проблему. Подробнее здесь: Как ответить
Дженсен