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


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

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

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]));

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

Можете ли вы подтвердить, что вам сначала нужно было установить "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

Если вам не очень нравятся вложенные функции, вы также можете перебирать ключи:

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 от машинописного текста.
Питер - Восстановить Монику
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.