Ітерація над Typescript картою


134

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

Мій код:

myMap : Map<string, boolean>;
for(let key of myMap.keys()) {
   console.log(key);
}

І я отримую Помилку:

Тип "IterableIteratorShim <[string, boolean]>" - це не тип масиву чи тип рядка.

Повна стежка стека:

 Error: Typescript found the following errors:
  /home/project/tmp/broccoli_type_script_compiler-input_base_path-q4GtzHgb.tmp/0/src/app/project/project-data.service.ts (21, 20): Type 'IterableIteratorShim<[string, boolean]>' is not an array type or a string type.
    at BroccoliTypeScriptCompiler._doIncrementalBuild (/home/project/node_modules/angular-cli/lib/broccoli/broccoli-typescript.js:115:19)
    at BroccoliTypeScriptCompiler.build (/home/project/node_modules/angular-cli/lib/broccoli/broccoli-typescript.js:43:10)
    at /home/project/node_modules/broccoli-caching-writer/index.js:152:21
    at lib$rsvp$$internal$$tryCatch (/home/project/node_modules/rsvp/dist/rsvp.js:1036:16)
    at lib$rsvp$$internal$$invokeCallback (/home/project/node_modules/rsvp/dist/rsvp.js:1048:17)
    at lib$rsvp$$internal$$publish (/home/project/node_modules/rsvp/dist/rsvp.js:1019:11)
    at lib$rsvp$asap$$flush (/home/project/node_modules/rsvp/dist/rsvp.js:1198:9)
    at _combinedTickCallback (internal/process/next_tick.js:67:7)
    at process._tickCallback (internal/process/next_tick.js:98:9)

Я використовую angular-cli beta5 та typecript 1.8.10, а моя мета - es5. Хтось мав цю проблему?


4
Дивіться цю відповідь від github github.com/Microsoft/TypeScript/isissue/…
yurzui

Відповіді:


210

Ви можете використати Map.prototype.forEach((value, key, map) => void, thisArg?) : voidнатомість

Використовуйте його так:

myMap.forEach((value: boolean, key: string) => {
    console.log(key, value);
});

15
Просто натрапив на це. Не схоже, що TypeScript поважає специфікацію для ітерації карти, принаймні згідно з MDN, який вказує цикл for-of. .forEachє недостатньо оптимальним, оскільки ви не можете його зламати, AFAIK
Samjones

14
не знаю, чому його значення тоді ключове. Здається назад.
Пол Руні

@PaulRooney це, мабуть, гармоніювати з цим Array.prototype.map.
Ахмед Фасіх

1
Як зупинити цей процес ітерації, якщо ми знайшли те, що нам потрібно?
JavaRunner

36

es6

for (let [key, value] of map) {
    console.log(key, value);
}

es5

for (let entry of Array.from(map.entries())) {
    let key = entry[0];
    let value = entry[1];
}

1
Версія es6, наведена вище, не складається в суворому режимі.
Стефан

Дякую, добрий пане.
Kitanga Nday

36

Просто скористайтеся Array.from()методом, щоб перетворити його на Array:

myMap : Map<string, boolean>;
for(let key of Array.from( myMap.keys()) ) {
   console.log(key);
}

5
Перетворення карт - це дуже ефективна операція, а не правильний спосіб вирішення проблеми, яка по суті є лише компілятором, який приховує основні частини мови. Просто <any>-введіть карту, щоб повторити її за допомогою.
Кільвес

1
Остерігайтеся: я думав, що пропозиція @Kilves, призначена вище, передати карту "будь-якій" - це елегантне вирішення. Коли я це зробив, код збирався та запускався без нарікань, але Карта насправді не ітеравалася - вміст циклу ніколи не виконувався. Array.from()Стратегія , запропонована тут зробили роботу для мене.
мактір

Я також спробував це, і мені це не вийшло, що навіть дурніше, вважаючи, що він є частиною ES6 і повинен "просто працювати" у більшості браузерів. Але я думаю, наші кутові оверделі використовують деякі чарівні мамбо джамбо в zone.js, щоб вони не спрацювали, оскільки вони ненавидять ES6. Зітхнути.
Кільвес

28

Використання функцій Array.from , Array.prototype.forEach () та стрілок :

Ітерація над ключами :

Array.from(myMap.keys()).forEach(key => console.log(key));

Ітерація над значеннями :

Array.from(myMap.values()).forEach(value => console.log(value));

Ітерація над записами :

Array.from(myMap.entries()).forEach(entry => console.log('Key: ' + entry[0] + ' Value: ' + entry[1]));

2
Не знаю чому, у мене така карта, як Map <String, CustomeClass>. жоден з перерахованих вище методів не працював, крім Array.from (myMap.values ​​()). forEach (value => console.log (value));
Rajashree Gr

27

Це працювало для мене. Версія TypeScript: 2.8.3

for (const [key, value] of Object.entries(myMap)) { 
    console.log(key, value);
}

7
Я змусив це працювати, змінивши Object.entries (myMap) на просто myMap.entries (). Мені подобається ця відповідь, тому що вона дозволяє уникнути помилок в роботі підводних каменів .forEach.
заграти

2
Варто зауважити, що якщо ваш вхід targetв tsconfigце, es5це призводить до помилки, але es6працює правильно. Ви також можете робити лише for (const [key, value] of myMap)при націлюванніes6
Бенджамін Воглер

16

Відповідно до приміток до випуску TypeScript 2.3 до "Нового --downlevelIteration" :

for..of statements, Елементи деструктуризації масиву та розповсюдження в Array, Call та New виразах підтримують Symbol.iterator в ES5 / E3, якщо вони доступні при використанні --downlevelIteration

Це не включено за замовчуванням! Додайте "downlevelIteration": trueдо свого tsconfig.jsonабо передайте --downlevelIterationпрапор tsc, щоб отримати повну підтримку ітератора.

З цим на місці, ви можете писати, for (let keyval of myMap) {...}а keyvalтип буде автоматично зроблений.


Чому це вимкнено за замовчуванням? За словами дописувача TypeScript @aluanhaddad ,

Це необов'язково, оскільки він дуже суттєво впливає на розмір згенерованого коду та, можливо, на продуктивність, для будь-якого використання ітерабелів (включаючи масиви).

Якщо ви можете націлити на ES2015 ( "target": "es2015"в tsconfig.jsonабо tsc --target ES2015) або пізнішої версії, увімкнення downlevelIterationне є мозком, але якщо ви орієнтуєтесь на ES5 / ES3, ви можете орієнтирувати, щоб переконатися, що підтримка ітератора не впливає на ефективність (якщо це так, можливо, ви будете кращими з Array.fromконверсією чи forEachіншим способом вирішення).


це нормально чи небезпечно включити це під час використання Angular?
Simon_Weaver

9

Це працювало для мене.

Object.keys(myMap).map( key => {
    console.log("key: " + key);
    console.log("value: " + myMap[key]);
});

2
При цьому ключі завжди будуть рядками
Ixx

8

Я використовую останні TS і вузол (v2.6 і v8.9 відповідно) і можу:

let myMap = new Map<string, boolean>();
myMap.set("a", true);
for (let [k, v] of myMap) {
    console.log(k + "=" + v);
}

Чи можете ви підтвердити, що спочатку вам довелося встановити "downlevelIteration": trueсвій tsconfig.json?
Ахмед Фасіх

3
Я не downlevelIteratonставив, моя мета, es2017проте.
lazieburd

Чи не можу це зробити для елемента Map <string, CustomClass []>? компілятор каже, що це не масив типу або рядка.
Rajashree Gr

це не працює для мене на 2.8 - я отримуюType 'Map<K, V>' is not an array type or a string type.
Simon_Weaver

3

Ви також можете застосувати метод карти масиву до ітерабельного Map.entries ():

[...myMap.entries()].map(
     ([key, value]: [string, number]) => console.log(key, value)
);

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

  "downlevelIteration": true,

Ця функція була введена в TypeScript 2.3. Проблема виникла з машинописом 1.8.10
MWe

Якщо ви не можете використовувати "downlevelIteration", ви можете використовувати:const projected = Array.from(myMap).map(...);
Efrain

1

Просто просте пояснення, щоб використовувати його в документі HTML.

Якщо у вас є карта типів (ключ, масив), ви ініціалізуєте масив таким чином:

public cityShop: Map<string, Shop[]> = new Map();

І щоб повторити його, ви створюєте масив з ключових значень.

Просто використовуйте його як масив, як у:

keys = Array.from(this.cityShop.keys());

Потім у HTML ви можете використовувати:

*ngFor="let key of keys"

Всередині цього циклу ви просто отримуєте значення масиву за допомогою:

this.cityShop.get(key)

Готово!



-1

Якщо вам не дуже подобаються вкладені функції, ви також можете переглядати клавіші:

myMap : Map<string, boolean>;
for(let key of myMap) {
   if (myMap.hasOwnProperty(key)) {
       console.log(JSON.stringify({key: key, value: myMap[key]}));
   }
}

Зауважте, що вам доведеться відфільтрувати не ключові ітерації за допомогою hasOwnProperty, якщо цього не зробити, ви отримаєте попередження або помилку.


як перебрати карту в HTML? це, здається, не працює взагалі. <div ng-repe = "(ключ, значення) в model.myMap"> {{key}}. </div>
Shinya Koizumi

@ powerfade917 Це не працює, він працює лише для масивів, оскільки angular - це купа сміття. Але задайте це як нове запитання, і так ви дізнаєтесь, що кутовий - це не стопка сміття, а вам доведеться перетворити його в масив. Зауважте, також ви не є вершиною у верхній частині програмування, тому що ви, здається, не в змозі розмежувати кутовий і машинопис.
петерх
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.