Цикл foreach Javascript на об'єкт асоціативного масиву


182

Чому мій for для кожного циклу не повторюється над моїм об’єктом асоціативного масиву 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]);
}

EDIT: jQuery each()може бути корисним: https://api.jquery.com/jQuery.each/


1
Ви створюєте масив, але потім використовуєте його як карту. Схоже, натомість ви хочете отримати звичайний об’єкт.
Джон

4
Немає таких речей, як associative arraysу JS: це або звичайний масив, або об'єкт. Ніщо не заважає додавати нечислові властивості до цього 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]);
}

редагувати - напевно, зараз добре зауважити, що Object.keys()функція доступна у сучасних браузерах та у вузлі тощо. Ця функція повертає "власні" клавіші об'єкта як масиву:

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 = {};? Я маю на увазі {}
Szymon Toda

1
@Ultra - це пустий буквальний об'єкт. Це просто означає, що змінна ініціалізується з посиланням на новий порожній об'єкт.
Pointy

@Ultra вам не потрібен екземпляр Array, оскільки в цьому коді ви не використовуєте його, як масив JavaScript. JavaScript не має "асоціативних масивів", як деякі інші мови.
Pointy

1
Яка різниця між призначенням []і {}? Тільки різні прототипи?
Незабаром

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в петлі.
припинення

2
@ceving, чи можете ви пояснити більше, чому б не використовувати var у циклах? Я походжу з C ++ / PHP, і я цього не розумію. скопінг існує в циклі, але тимчасовий, тому я не впевнений, що ви маєте на увазі.
Денніс

6
@Dennis У більшості мов змінна декларація в forциклі створює область, обмежену тілом forциклу. Але в JavaScript змінна, яку оголошує, varзавжди функціонує глобально, навіть якщо ви пишете її в forциклі. Дивіться тут: davidwalsh.name/for-and-against-let
ceving

1
не протікає пам'ять сама по собі, але, безумовно, "розливається пам'ять". varу Javascript створює змінні в пам'яті навіть після циклу.
ahnbizcad

Я думаю, що сенс цих коментарів щодо використання "var" полягає в тому, щоб зараз використовувати "нехай" взагалі, але конкретно в циклі. Причини пояснюються вище, але, коротко кажучи, це обумовлено сферою застосування, "var" створює глобальну сферу застосування.
DeeZone

6

Наведено вже декілька простих прикладів, але я помічаю, як ви сформулювали своє запитання про те, що ви, ймовірно, походите з фонового режиму PHP, і ви очікуєте, що JavaScript працює так само - це не так. PHP arrayдуже відрізняється від JavaScript Array.

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

У JavaScript все є об'єктом (крім примітивів: string, numeric, boolean), а масиви - це певна реалізація, яка дозволяє мати числові індекси. Все , що штовхнув в масив буде вплив на його 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

Ось простий спосіб використання асоціативного масиву як загального типу об’єкта:

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


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використовується призначення деструктування масиву.


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

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)
})
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.