Javascript цикл foreach на объекте ассоциативного массива

182

Почему цикл for for-each не выполняет итерацию по моему объекту ассоциативного массива JavaScript?

// defining an array
var array = [];

// assigning values to corresponding keys
array["Main"] = "Main page";
array["Guide"] = "Guide page";
array["Articles"] = "Articles page";
array["Forum"] = "Forum board";

// expected: loop over every item,
// yet it logs only "last" assigned value - "Forum"
for (var i = 0; i < array.length; i++) {
    console.log(array[i]);
}

РЕДАКТИРОВАТЬ: JQuery each()может быть полезным: https://api.jquery.com/jQuery.each/

Шимон Тода
источник
1
Вы создаете массив, но затем используете его как карту. Похоже, вы хотите простой объект вместо.
Джон
4
В associative arraysJS нет таких вещей : это либо обычный массив, либо объект. Ничто не мешает добавлять нечисловые свойства Array, но это не делает этого associative- в частности, lengthсвойство не будет автоматически считать эти свойства.
raina77ow
3
Re: В JS нет таких вещей, как ассоциативные массивы. - сформулирован иначе: JavaScript использует имя «объект» вместо имени «ассоциативный массив». Но у него нет свойства ".length".
Джесси Чисхолм

Ответы:

315

.lengthСвойство отслеживает только свойство с числовыми индексами (ключи). Вы используете строки для ключей.

Ты можешь сделать это:

var arr_jq_TabContents = {}; // no need for an array

arr_jq_TabContents["Main"] = jq_TabContents_Main;
arr_jq_TabContents["Guide"] = jq_TabContents_Guide;
arr_jq_TabContents["Articles"] = jq_TabContents_Articles;
arr_jq_TabContents["Forum"] = jq_TabContents_Forum;

for (var key in arr_jq_TabContents) {
    console.log(arr_jq_TabContents[key]);
}

Чтобы быть в безопасности, рекомендуется в таких циклах убедиться, что ни одно из свойств не является неожиданным результатом наследования:

for (var key in arr_jq_TabContents) {
  if (arr_jq_TabContents.hasOwnProperty(key))
    console.log(arr_jq_TabContents[key]);
}

edit - вероятно, сейчас стоит отметить, что Object.keys()функция доступна в современных браузерах, в Node и т. д. Эта функция возвращает «собственные» ключи объекта в виде массива:

Object.keys(arr_jq_TabContents).forEach(function(key, index) {
  console.log(this[key]);
}, arr_jq_TabContents);

Функция обратного вызова, передаваемая в, .forEach()вызывается с каждым ключом, а индекс ключа в массиве возвращается Object.keys(). Также передается массив, через который проходит функция, но этот массив нам не очень полезен; нам нужен оригинальный объект . К нему можно получить доступ напрямую по имени, но (на мой взгляд) немного приятнее передать его явно, что достигается передачей второго аргумента .forEach()- исходному объекту - который будет связан как thisвнутри обратного вызова. (Просто увидел, что это было отмечено в комментарии ниже.)

Заостренный
источник
Что это var arr_jq_TabContents = {};? Я имею в виду {}
Шимон Тода
1
@Ultra это пустой литерал объекта. Это просто означает, что переменная инициализируется ссылкой на новый пустой объект.
Заостренный
@ Ультра вам не нужен экземпляр Array, потому что в этом коде вы не используете его как массив JavaScript. JavaScript не имеет «ассоциативных массивов», как некоторые другие языки.
Заостренный
1
В чем разница между назначением []и {}? Только разные прототипы?
SoonDead
8
Вместо использования hasOwnProperty во всех современных веб-браузерах должна быть возможность перебирать ключи объекта, используя: Object.keys(arr_jq_TabContents).forEach( function(key) { ... } );
Gregory Bell
80

Это очень простой подход. Преимущество в том, что вы также можете получить ключи:

for (var key in array) {
    var value = array[key];
    console.log(key, value);
}

Для ES6:

array.forEach(value => {
  console.log(value)
})  

Для ES6: (если вам нужно значение, индекс и сам массив)

array.forEach((value, index, self) => {
  console.log(value, index, self)
})  
ТИКА
источник
12
Не используйте varв циклах, предлагающих область видимости, которая не существует. Используйте varперед петлей или letв петле.
ceving
2
@ceving, не могли бы вы объяснить, почему бы не использовать var в циклах? Я родом из C ++ / PHP и не понимаю этого. область видимости существует в цикле, но временная, поэтому я не уверен, что вы имеете в виду.
Деннис
6
@Dennis В большинстве языков объявление переменной в forцикле генерирует область видимости, ограниченную телом forцикла. Но в JavaScript объявленная переменная varвсегда является функцией global, даже если вы пишете ее в forцикле. Смотрите здесь: davidwalsh.name/for-and-against-let
Ceving
1
не утечки памяти как таковые, но определенно "разливы памяти". varв Javascript создает переменные в памяти даже после завершения цикла.
Анбизкад
Я думаю, что смысл этих комментариев, касающихся использования «var», заключается в том, чтобы теперь использовать «let» в целом, но конкретно в цикле. Причины объяснены выше, но в итоге это связано с областью видимости, "var" создает глобальную область видимости.
DeeZone
6

Уже есть несколько простых примеров, но я заметил из того, как вы сформулировали свой вопрос, что вы, вероятно, пришли из фона PHP, и вы ожидаете, что JavaScript будет работать таким же образом - это не так. PHP arrayсильно отличается от JavaScriptArray .

В PHP ассоциативный массив может делать большую часть того, что может численно индексированный массив ( array_*функции работают, вы можетеcount() это и т. Д.). Вы просто создаете массив и начинаете присваивать строковые индексы вместо числовых.

В JavaScript все является объектом (кроме примитивов: строковых, числовых, логических), а массивы представляют собой определенную реализацию, которая позволяет иметь числовые индексы. Все , что толкнул в массив будет влияние на его length, и может повторяться по сравнению с использованием методов Array ( map, forEach, reduce, filter, findи т.д.) Однако, поскольку все является объектом, вы всегда свободны просто присвоить свойства, потому что это то , что вы делаете , чтобы любой объект. Обозначение в квадратных скобках - это просто еще один способ получить доступ к свойству, поэтому в вашем случае:

array['Main'] = 'Main Page';

на самом деле эквивалентно:

array.Main = 'Main Page';

Из вашего описания я предполагаю, что вам нужен «ассоциативный массив», но для JavaScript это простой случай использования объекта в качестве хэш-карты. Кроме того, я знаю, что это пример, но избегайте бессмысленных имен, которые описывают только тип переменной (например array), и имя, основанное на том, что оно должно содержать (например pages). Простые объекты не имеют много хороших прямых путей к итерация, так часто мы повернем затем в массивы первых , используя Objectметоды ( Object.keysв данном случае - есть также entriesи valuesдобавляется в некоторых браузерах прямо сейчас) , которые мы можем цикл.

// assigning values to corresponding keys
const pages = {
  Main: 'Main page',
  Guide: 'Guide page',
  Articles: 'Articles page',
  Forum: 'Forum board',
};

Object.keys(pages).forEach((page) => console.log(page));
Джош из Карибу
источник
4

arr_jq_TabContents[key] видит массив как форму с 0 индексами.

MSO
источник
4

Вот простой способ использовать ассоциативный массив в качестве универсального типа объекта:

Object.prototype.forEach = function(cb){
   if(this instanceof Array) return this.forEach(cb);
   let self = this;
   Object.getOwnPropertyNames(this).forEach(
      (k)=>{ cb.call(self, self[k], k); }
   );
};

Object({a:1,b:2,c:3}).forEach((value, key)=>{ 
    console.log(`key/value pair: ${key}/${value}`);
});

Chouettou
источник
4

Если node.js или браузер поддерживается Object.entries(), его можно использовать в качестве альтернативы использованию Object.keys()( https://stackoverflow.com/a/18804596/225291 ).

const h = {
  a: 1,
  b: 2
};

Object.entries(h).forEach(([key, value]) => console.log(value));
// logs 1, 2

в этом примере forEachиспользуется Destructuring присваивание массива.

маклер
источник
1

Это (по сути) неправильно в большинстве случаев:

var array = [];
array["Main"] = "Main page";

Это создает неэлементное свойство в массиве с именем Main. Хотя массивы являются объектами, обычно вы не хотите создавать для них неэлементные свойства.

Если вы хотите индексировать arrayпо этим именам, обычно вы используете Mapпростой объект или объект, а не массив.

С Map(ES2015 +), который я назову, mapпотому что я креативен :

let map = new Map();
map.set("Main", "Main page");

вы затем перебирать его , используя итераторы из своих values, keys, или entriesспособов, например:

for (const value of map.values()) {
    // Here, `value` will be `"Main page"`, etc.
}

Используя простой объект, который я творчески назову obj:

let obj = Object.create(null); // Creates an object with no prototype
obj.Main = "Main page"; // Or: `obj["Main"] = "Main page";`

вы затем итерацию его содержимое с помощью Object.keys, Object.valuesили Object.entries, например:

for (const value of Object.values(proches_X)) {
    // Here, `value` will be `"Main page"`, etc.
}
TJ Crowder
источник
0

var obj = {
  no: ["no", 32],
  nt: ["no", 32],
  nf: ["no", 32, 90]
};

count = -1; // which must be static value
for (i in obj) {
  count++;
  if (obj.hasOwnProperty(i)) {
    console.log(obj[i][count])
  };
};

в этом коде я использовал метод скобок для вызова значений в массиве, потому что он содержал массив, однако вкратце идея о том, что переменная i имеет ключ свойства и с циклом, который называет оба значения ассоциированного массива

Идеальный метод, если вам интересно, нажмите как

Дев-Вб Ахмед
источник
Вы не зацикливаете массив, вы зацикливаете объект, у которого есть массивы в его ключах. Пожалуйста, прочитайте вопрос и посмотрите на пример.
Педро Хоакин
0

Ты можешь сделать это

var array = [];

// assigning values to corresponding keys
array[0] = "Main page";
array[1] = "Guide page";
array[2] = "Articles page";
array[3] = "Forum board";


array.forEach(value => {
    console.log(value)
})
Shuvro
источник