반복기에서 map () 사용


87

Map :이 있다고 가정 let m = new Map();하면 using m.values()은 맵 반복자 를 반환합니다.

하지만 사용할 수 없습니다 forEach()또는 map()그 반복자와 반복자 같은 ES6의 서비스 기능 이후 안티 패턴처럼 보인다에서 잠시 루프를 구현하는 방법에 대한 map().

map()반복자 에서 사용할 수있는 방법이 있습니까?


즉시 사용할 수는 없지만 lodash mapMap을 지원하는 기능 과 같은 타사 라이브러리도 사용할 수 있습니다 .
유해한

맵 자체에는 키-값 쌍을 반복 하는 forEach 가 있습니다.
유해한

반복자를 배열로 변환하고 Array.from(m.values()).map(...)작동 하는 것처럼 매핑 하지만 이것이 최선의 방법은 아니라고 생각합니다.
JiminP 2017 년

배열이 사용하기에 더 적합 할 때 반복기를 사용하여 해결해야 할 문제는 Array#map무엇입니까?
Nina Scholz

1
@NinaScholz 저는 다음과 같은 일반 세트를 사용하고 있습니다 : stackoverflow.com/a/29783624/4279201
shinzou

답변:


80

이를 수행하는 가장 간단 하고 성능가장 낮은 방법은 다음과 같습니다.

Array.from(m).map(([key,value]) => /* whatever */)

더 나은

Array.from(m, ([key, value]) => /* whatever */))

Array.from반복 가능하거나 배열과 유사한 것을 취하여 배열로 변환합니다! Daniel이 주석에서 지적했듯이 변환에 매핑 함수를 추가하여 반복을 제거하고 이후에 중간 배열을 제거 할 수 있습니다.

사용 Array.from에서 성능 이동 O(1)O(n)코멘트에 밖으로 @hraban 점으로. 이후 mA는 Map, 그들이 무한 수 없습니다, 우리는 무한 순서에 대해 걱정할 필요가 없습니다. 대부분의 경우 이것으로 충분합니다.

지도를 반복하는 몇 가지 다른 방법이 있습니다.

사용 forEach

m.forEach((value,key) => /* stuff */ )

사용 for..of

var myMap = new Map();
myMap.set(0, 'zero');
myMap.set(1, 'one');
for (var [key, value] of myMap) {
  console.log(key + ' = ' + value);
}
// 0 = zero
// 1 = one

지도의 길이가 무한 할 수 있습니까?
ktilcu

2
반복자의 경우 @ktilcu : 예. 반복기의 .map은 반복기 자체를 반환하는 생성기의 변환으로 생각할 수 있습니다. 하나의 요소를 팝하면 기본 반복기를 호출하고 요소를 변환하여 반환합니다.
hraban

7
이 답변의 문제는 O (1) 메모리 알고리즘이 될 수있는 것을 O (n)으로 바꾸는 것입니다. 이는 더 큰 데이터 세트에 대해 매우 심각한 문제입니다. 물론 유한하고 스트리밍 불가능한 반복자가 필요합니다. 질문의 제목은 "Using map () on an iterator"이며, 게으른 무한 시퀀스가 ​​질문의 일부가 아니라는 데 동의하지 않습니다. 이것이 바로 사람들이 반복자를 사용하는 방법입니다. "지도"는 단지 예일뿐입니다 ( "Say .."). 이 답변의 좋은 점은 매우 중요합니다.
hraban

1
@hraban이 토론에 추가해 주셔서 감사합니다. 나는 미래의 여행자들이 정보의 전면과 중앙을 가질 수 있도록 몇 가지주의 사항을 포함하도록 답변을 업데이트 할 수 있습니다. 그것에 관해서 우리는 종종 간단한 성능과 최적 성능 사이에서 결정을 내려야 할 것입니다. 나는 일반적으로 성능보다 더 간단한 (디버그, 유지, 설명) 편을들 것입니다.
ktilcu 2017

3
@ktilcu 대신 호출 Array.from(m, ([key,value]) => /* whatever */)(매핑 함수가 내부에 있음 from)을 호출 하면 중간 배열이 생성되지 않습니다 ( source ). 여전히 O (1)에서 O (n)으로 이동하지만 최소한 한 번의 전체 반복에서 반복과 매핑이 발생합니다.
다니엘

18

이를 반복하기 위해 다른 반복기 함수를 정의 할 수 있습니다.

function* generator() {
    for(let i = 0; i < 10; i++) {
        console.log(i);
        yield i;
    }
}

function* mapIterator(iterator, mapping) {
    while (true) {
        let result = iterator.next();
        if (result.done) {
            break;
        }
        yield mapping(result.value);
    }
}

let values = generator();
let mapped = mapIterator(values, (i) => {
    let result = i*2;
    console.log(`x2 = ${result}`);
    return result;
});

console.log('The values will be generated right now.');
console.log(Array.from(mapped).join(','));

이제 질문 할 수 있습니다. Array.from대신 사용하지 않는 이유는 무엇입니까? 이것은 전체 반복자를 통해 실행되므로 (임시) 배열에 저장하고 다시 반복 한 다음 매핑 수행합니다. 목록이 방대하거나 잠재적으로 무한한 경우 불필요한 메모리 사용량이 발생합니다.

물론 항목 목록이 상당히 적 으면 사용 Array.from만으로도 충분합니다.


유한 한 양의 메모리가 어떻게 무한한 데이터 구조를 유지할 수 있습니까?
shinzou

3
그렇지 않습니다. 그게 요점입니다. 이를 사용하여 반복기 소스를 여러 반복기 변환 및 마지막으로 소비자 싱크에 연결하여 "데이터 스트림"을 생성 할 수 있습니다. 예 : 스트리밍 오디오 처리, 대용량 파일 작업, 데이터베이스의 애그리 게이터 등
hraban

1
나는이 대답을 좋아한다. 누구나 iterables에서 Array와 유사한 메소드를 제공하는 라이브러리를 추천 할 수 있습니까?
Joel Malone

1
mapIterator()iterator.return()반환 값의 next가 한 번 이상 호출되지 않는 한 기본 반복기가 제대로 닫히 ( 호출) 보장되지 않습니다 . 참조 : repeater.js.org/docs/safety
Jaka Jančar

11

이 가장 간단하고 가장 효과적인 방법은 두 번째 인수를 사용 Array.from하여이를 달성하는 것입니다.

const map = new Map()
map.set('a', 1)
map.set('b', 2)

Array.from(map, ([key, value]) => `${key}:${value}`)
// ['a:1', 'b:2']

이 접근 방식은 무한 하지 않은 모든 반복 가능 항목에 적용됩니다. 그리고 Array.from(map).map(...)iterable을 두 번 반복하고 성능이 더 나빠지 는 별도의 호출을 사용할 필요가 없습니다 .


3

이터 러블에 대한 이터레이터를 검색 한 다음 각 반복 요소에 대해 매핑 콜백 함수를 호출하는 또 다른 이터레이터를 반환 할 수 있습니다.

const map = (iterable, callback) => {
  return {
    [Symbol.iterator]() {
      const iterator = iterable[Symbol.iterator]();
      return {
        next() {
          const r = iterator.next();
          if (r.done)
            return r;
          else {
            return {
              value: callback(r.value),
              done: false,
            };
          }
        }
      }
    }
  }
};

// Arrays are iterable
console.log(...map([0, 1, 2, 3, 4], (num) => 2 * num)); // 0 2 4 6 8

2

iterables에 대해 배열과 유사한 메소드를 구현 하는 itiriri 를 사용할 수 있습니다 .

import { query } from 'itiriri';

let m = new Map();
// set map ...

query(m).filter([k, v] => k < 10).forEach([k, v] => console.log(v));
let arr = query(m.values()).map(v => v * 10).toArray();

좋은! 이것이 JS의 API가 수행되어야하는 방법입니다. 언제나 그렇듯이 Rust는 옳습니다 : doc.rust-lang.org/std/iter/trait.Iterator.html
Flying sheep

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.