Итерация по карте машинописного текста

135

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

Мой код:

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 и typescript 1.8.10, и моя цель - es5. У кого-нибудь была эта проблема?

м.в.э.
источник
4
См. Этот ответ от github github.com/Microsoft/TypeScript/issues/…
yurzui

Ответы:

212

Map.prototype.forEach((value, key, map) => void, thisArg?) : voidВместо этого вы можете использовать

Используйте это так:

myMap.forEach((value: boolean, key: string) => {
    console.log(key, value);
});
rinukkusu
источник
15
Просто столкнулся с этим. Не похоже, что TypeScript соблюдает спецификацию для итерации карты, по крайней мере, согласно MDN, который определяет цикл for-of. .forEachнеоптимально, так как вы не можете его сломать,
AFAIK
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 не компилируется в строгом режиме.
Стефан
Спасибо, добрый сэр.
Китанга Ндай,
36

Просто используйте Array.from()метод, чтобы преобразовать его в Array:

myMap : Map<string, boolean>;
for(let key of Array.from( myMap.keys()) ) {
   console.log(key);
}
6qat
источник
5
Преобразование карт - это очень требовательная к производительности операция, и это не правильный способ решить проблему, которая заключается в том, что компилятор просто скрывает основные части языка. Просто <any>приведите карту, чтобы повторить ее с помощью for-of.
Килвс 06
1
Осторожно: я подумал, что предложение @Kilves, приведенное выше, о преобразовании карты в «любую», было элегантным обходным путем. Когда я это сделал, код компилировался и запускался без жалоб, но Map фактически не повторялся - содержимое цикла никогда не выполнялось. Array.from()Стратегия , предложенная здесь сделали работу для меня.
mactyr
Я тоже пробовал это, у меня это тоже не сработало, что даже глупее, учитывая, что он является частью ES6 и должен «просто работать» в большинстве браузеров. Но я предполагаю, что наши угловые повелители используют какую-то волшебную чепуху в zone.js, чтобы заставить его не работать, потому что они ненавидят ES6. Вздох.
Kilves
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]));
Tobold
источник
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
Стоит отметить, что если ваш targetin tsconfigis, es5это выдает ошибку, но с es6работает правильно. Вы также можете просто сделать for (const [key, value] of myMap)при нацеливанииes6
Бенджамин Фоглер
16

Согласно примечаниям к выпуску TypeScript 2.3 на «Новом --downlevelIteration» :

for..of statementsЭлементы Array Destructuring и Spread в выражениях 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 и node (v2.6 и v8.9 соответственно), и я могу:

let myMap = new Map<string, boolean>();
myMap.set("a", true);
for (let [k, v] of myMap) {
    console.log(k + "=" + v);
}
lazieburd
источник
Можете ли вы подтвердить, что вам сначала нужно было установить "downlevelIteration": trueсвой tsconfig.json?
Ахмед Фасих
3
Я не downlevelIteratonставил, но моя цель есть es2017.
lazieburd
Разве я не могу сделать это для элемента Map <string, CustomClass []>? компилятор говорит, что это не массив типа или типа строки.
Rajashree Gr 05
у меня это не работает на 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. Проблема возникла с TypeScript 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

В Typescript 3.5 и Angular 8 LTS требовалось привести тип следующим образом:

for (let [k, v] of Object.entries(someMap)) {
    console.log(k, v)
}
Константин Константинидис
источник
-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-repeat = "(ключ, значение) в model.myMap"> {{ключ}}. </div>
Шинья Коидзуми
@ powerfade917 Это не работает, работает только с массивами, потому что angular - это куча мусора. Но задайте это как новый вопрос, и вы узнаете, что angular - это не куча мусора, но вы должны преобразовать его в массив. Обратите внимание: вы также не на вершине программирования, потому что, по-видимому, вы не способны отличить angular от машинописного текста.
Питер - Восстановить Монику