Javascript 반복자를 배열로 변환


171

최신 Firefox 및 Chrome 버전에서 이미 지원되므로 Javascript EC6 의 새 Map 객체 를 사용하려고합니다 .

그러나 고전적인 맵, 필터 등 [key, value]쌍이 잘 작동하는 메소드가 없기 때문에 "기능"프로그래밍에서는 매우 제한적입니다 . forEach가 있지만 콜백 결과를 리턴하지 않습니다.

나는 그것의 변환 수 있다면 map.entries()간단한 배열로 MapIterator에서 나는 다음 표준을 사용할 수 .map, .filter추가 해킹으로.

Javascript Iterator를 Array로 변환하는 "좋은"방법이 있습니까? 파이썬에서는 그렇게하는 list(iterator)것이 쉽지만 ... Array(m.entries())Iterator를 첫 번째 요소로 사용하여 배열을 반환하십시오!

편집하다

지도가 작동하는 곳에서 작동하는 답변을 찾고 있음을 지정하는 것을 잊었습니다. 최소한 Chrome 및 Firefox를 의미합니다 (Array.from은 Chrome에서 작동하지 않음).

추신.

환상적인 wu.js 가 있다는 것을 알고 있지만 traceur에 대한 의존성은 나를 미치게 합니다 ...


답변:


247

임의의 이터 러블을 배열 인스턴스로 변환 하는 새로운 Array.from함수 를 찾고 있습니다 .

var arr = Array.from(map.entries());

이제 Edge, FF, Chrome 및 Node 4+에서 지원됩니다 .

물론, 정의 할 가치가있을 수도 있습니다 map, filter당신은 배열을 할당 피할 수 그래서, 직접 반복자 인터페이스에 유사한 방법. 고차 함수 대신 생성기 함수를 사용할 수도 있습니다.

function* map(iterable) {
    var i = 0;
    for (var item of iterable)
        yield yourTransformation(item, i++);
}
function* filter(iterable) {
    var i = 0;
    for (var item of iterable)
        if (yourPredicate(item, i++))
             yield item;
}

콜백은 (value, key)쌍이 아닌 (value, index)쌍 을받을 것으로 기대합니다 .
Aadit M Shah

3
@ AaditMShah : 반복자의 열쇠는 무엇입니까? 물론,지도를 반복한다면 다음을 정의 할 수 있습니다.yourTransformation = function([key, value], index) { … }
Bergi

반복자에는 키가 없지만 a Map에는 키 값 쌍이 있습니다. 따라서 겸손한 의견으로 는 반복자에 대한 일반 mapfilter함수 를 정의하는 것은 의미가 없습니다 . 대신, 각 반복 가능한 객체는 자신이 있어야 map하고 filter기능을. 이 의미가 있기 때문에 mapfilter구조 (아마하지 작업을 보존되어 filter있지만, map확실히이다) 따라서 mapfilter기능들이 이상 매핑 또는 필터링하는 것을 반복 가능한 객체의 구조를 알아야한다. 생각해보십시오. Haskell에서 우리는 서로 다른 인스턴스를 정의합니다 Functor. =)
Aadit M Shah 13:25에

1
@ 스테파노 : 당신은 쉽게 심을 수 있습니다 ...
Bergi

1
@Incognito Ah 알았어, 사실이지만, 그것은 내 대답에 문제가 아니라 질문이 요구하는 것입니다.
Bergi

45

[...map.entries()] 또는 Array.from(map.entries())

매우 쉽습니다.

어쨌든 반복자에는 감소, 필터링 및 유사한 방법이 부족합니다. Map을 배열로 변환하는 것보다 성능이 우수하므로 직접 작성해야합니다. 그러나 Map-> Array-> Map-> Array-> Map-> Array로 점프하지 마십시오. 성능이 저하 될 수 있습니다.


1
더 실질적인 것이 없다면, 이것은 실제로 주석이어야합니다. 또한 Array.from@Bergi가 이미 다루었습니다.
Aadit M Shah

2
그리고 원래 질문에 쓴 것처럼 [iterator]Chrome에서 단일 iterator요소 가 포함 된 배열을 만들고 Chrome [...map.entries()]에서 허용되는 구문이 아니기 때문에 작동하지 않습니다.
Stefano

2
@Stefano 스프레드 연산자 는 이제 크롬에서 허용되는 구문입니다.
Klesun

15

을 변환 할 필요가 없습니다 Map으로는 Array. 당신은 간단하게 만들 수 mapfilter대한 기능을 Map제품 :

function map(functor, object, self) {
    var result = new Map;

    object.forEach(function (value, key, object) {
        result.set(key, functor.call(this, value, key, object));
    }, self);

    return result;
}

function filter(predicate, object, self) {
    var result = new Map;

    object.forEach(function (value, key, object) {
        if (predicate.call(this, value, key, object)) result.set(key, value);
    }, self);

    return result;
}

예를 들어 !키가 기본 요소 인 맵의 각 항목 값에 뱅 (예 : 문자)을 추가 할 수 있습니다.

var object = new Map;

object.set("", "empty string");
object.set(0,  "number zero");
object.set(object, "itself");

var result = map(appendBang, filter(primitive, object));

alert(result.get(""));     // empty string!
alert(result.get(0));      // number zero!
alert(result.get(object)); // undefined

function primitive(value, key) {
    return isPrimitive(key);
}

function appendBang(value) {
    return value + "!";
}

function isPrimitive(value) {
    var type = typeof value;
    return value === null ||
        type !== "object" &&
        type !== "function";
}
<script>
function map(functor, object, self) {
    var result = new Map;

    object.forEach(function (value, key, object) {
        result.set(key, functor.call(this, value, key, object));
    }, self || null);

    return result;
}

function filter(predicate, object, self) {
    var result = new Map;

    object.forEach(function (value, key, object) {
        if (predicate.call(this, value, key, object)) result.set(key, value);
    }, self || null);

    return result;
}
</script>

당신은 또한 추가 할 수 있습니다 mapfilter의 방법은 Map.prototype더 나은 읽을 수 있도록. 일반적으로 아직 기본 프로토 타입을 수정하는 것이 좋습니다되지는 않지만 나는 예외의 경우에 할 수 있다고 생각 map하고 filter대한 Map.prototype:

var object = new Map;

object.set("", "empty string");
object.set(0,  "number zero");
object.set(object, "itself");

var result = object.filter(primitive).map(appendBang);

alert(result.get(""));     // empty string!
alert(result.get(0));      // number zero!
alert(result.get(object)); // undefined

function primitive(value, key) {
    return isPrimitive(key);
}

function appendBang(value) {
    return value + "!";
}

function isPrimitive(value) {
    var type = typeof value;
    return value === null ||
        type !== "object" &&
        type !== "function";
}
<script>
Map.prototype.map = function (functor, self) {
    var result = new Map;

    this.forEach(function (value, key, object) {
        result.set(key, functor.call(this, value, key, object));
    }, self || null);

    return result;
};

Map.prototype.filter = function (predicate, self) {
    var result = new Map;

    this.forEach(function (value, key, object) {
        if (predicate.call(this, value, key, object)) result.set(key, value);
    }, self || null);

    return result;
};
</script>


편집 : Bergi의 대답에서 그는 모든 반복 가능한 객체에 대해 일반 mapfilter생성기 함수를 만들었습니다 . 그것들을 사용하는 장점은 생성기 함수이므로 중간 반복 가능 객체를 할당하지 않는다는 것입니다.

예를 들어, 위에서 정의한 my mapfilter함수는 새 Map객체를 만듭니다 . 따라서 호출 object.filter(primitive).map(appendBang)하면 두 개의 새로운 Map객체 가 생성 됩니다.

var intermediate = object.filter(primitive);
var result = intermediate.map(appendBang);

반복 가능한 중간 객체를 만드는 것은 비용이 많이 듭니다. Bergi의 발전기 기능이이 문제를 해결합니다. 중간 객체를 할당하지 않지만 하나의 반복자가 값을 느리게 다음 값으로 공급할 수 있습니다. 이러한 종류의 최적화는 기능적 프로그래밍 언어에서 융합 또는 삼림 벌채 로 알려져 있으며 프로그램 성능을 크게 향상시킬 수 있습니다.

Bergi의 생성기 기능에서 내가 가진 유일한 문제는 그것들이 Map객체에 국한되지 않는다는 것 입니다. 대신, 반복 가능한 모든 객체에 대해 일반화됩니다. 따라서 (value, key)쌍으로 콜백 함수를 호출하는 대신 (을 통해 매핑 할 때 예상 되는대로 Map) (value, index)쌍으로 콜백 함수를 호출합니다 . 그렇지 않으면 훌륭한 솔루션이므로 제공된 솔루션보다 확실히 사용하는 것이 좋습니다.

따라서 이들은 Map객체 매핑 및 필터링에 사용할 특정 생성기 함수입니다 .

function * map(functor, entries, self) {
    var that = self || null;

    for (var entry of entries) {
        var key   = entry[0];
        var value = entry[1];

        yield [key, functor.call(that, value, key, entries)];
    }
}

function * filter(predicate, entries, self) {
    var that = self || null;

    for (var entry of entries) {
        var key    = entry[0];
        var value  = entry[1];

        if (predicate.call(that, value, key, entries)) yield [key, value];
    }
}

function toMap(entries) {
    var result = new Map;

    for (var entry of entries) {
        var key   = entry[0];
        var value = entry[1];

        result.set(key, value);
    }

    return result;
}

function toArray(entries) {
    var array = [];

    for (var entry of entries) {
        array.push(entry[1]);
    }

    return array;
}

다음과 같이 사용할 수 있습니다.

var object = new Map;

object.set("", "empty string");
object.set(0,  "number zero");
object.set(object, "itself");

var result = toMap(map(appendBang, filter(primitive, object.entries())));

alert(result.get(""));     // empty string!
alert(result.get(0));      // number zero!
alert(result.get(object)); // undefined

var array  = toArray(map(appendBang, filter(primitive, object.entries())));

alert(JSON.stringify(array, null, 4));

function primitive(value, key) {
    return isPrimitive(key);
}

function appendBang(value) {
    return value + "!";
}

function isPrimitive(value) {
    var type = typeof value;
    return value === null ||
        type !== "object" &&
        type !== "function";
}
<script>
function * map(functor, entries, self) {
    var that = self || null;

    for (var entry of entries) {
        var key   = entry[0];
        var value = entry[1];

        yield [key, functor.call(that, value, key, entries)];
    }
}

function * filter(predicate, entries, self) {
    var that = self || null;

    for (var entry of entries) {
        var key    = entry[0];
        var value  = entry[1];

        if (predicate.call(that, value, key, entries)) yield [key, value];
    }
}

function toMap(entries) {
    var result = new Map;

    for (var entry of entries) {
        var key   = entry[0];
        var value = entry[1];

        result.set(key, value);
    }

    return result;
}

function toArray(entries) {
    var array = [];

    for (var entry of entries) {
        array.push(entry[1]);
    }

    return array;
}
</script>

보다 유창한 인터페이스를 원한다면 다음과 같이 할 수 있습니다.

var object = new Map;

object.set("", "empty string");
object.set(0,  "number zero");
object.set(object, "itself");

var result = new MapEntries(object).filter(primitive).map(appendBang).toMap();

alert(result.get(""));     // empty string!
alert(result.get(0));      // number zero!
alert(result.get(object)); // undefined

var array  = new MapEntries(object).filter(primitive).map(appendBang).toArray();

alert(JSON.stringify(array, null, 4));

function primitive(value, key) {
    return isPrimitive(key);
}

function appendBang(value) {
    return value + "!";
}

function isPrimitive(value) {
    var type = typeof value;
    return value === null ||
        type !== "object" &&
        type !== "function";
}
<script>
MapEntries.prototype = {
    constructor: MapEntries,
    map: function (functor, self) {
        return new MapEntries(map(functor, this.entries, self), true);
    },
    filter: function (predicate, self) {
        return new MapEntries(filter(predicate, this.entries, self), true);
    },
    toMap: function () {
        return toMap(this.entries);
    },
    toArray: function () {
        return toArray(this.entries);
    }
};

function MapEntries(map, entries) {
    this.entries = entries ? map : map.entries();
}

function * map(functor, entries, self) {
    var that = self || null;

    for (var entry of entries) {
        var key   = entry[0];
        var value = entry[1];

        yield [key, functor.call(that, value, key, entries)];
    }
}

function * filter(predicate, entries, self) {
    var that = self || null;

    for (var entry of entries) {
        var key    = entry[0];
        var value  = entry[1];

        if (predicate.call(that, value, key, entries)) yield [key, value];
    }
}

function toMap(entries) {
    var result = new Map;

    for (var entry of entries) {
        var key   = entry[0];
        var value = entry[1];

        result.set(key, value);
    }

    return result;
}

function toArray(entries) {
    var array = [];

    for (var entry of entries) {
        array.push(entry[1]);
    }

    return array;
}
</script>

희망이 도움이됩니다.


감사합니다! 비록 "Array.from"을 몰랐기 때문에 @Bergi에게 좋은 대답을 줬습니다. 그래도 매우 흥미로운 토론입니다!
Stefano

1
@Stefano 나는 생성기를 사용 Map하여 특수 map하고 filter함수를 사용하여 객체 를 올바르게 변환하는 방법을 보여주기 위해 대답을 편집했습니다 . Bergi의 답변은 객체의 키 가 손실 되어 객체 를 변환하는 데 사용할 수없는 모든 반복 가능한 객체 에 일반 함수 mapfilter함수를 사용하는 것을 보여줍니다 . MapMap
Aadit M Shah

와우, 난 당신의 편집을 정말 좋아합니다. Chrome에서 작동하지 않기 때문에 stackoverflow.com/a/28721418/422670 (이 질문이 복제본으로 폐쇄 된 이후 추가됨)에 내 답변을 작성했습니다 Array.from. 그러나 접근 방식이 매우 비슷하다는 것을 알 수 있으며 "toArray"함수를 무리에 추가 할 수 있습니다!
Stefano

1
@Stefano 참으로. toArray함수 를 추가하는 방법을 보여주기 위해 답변을 편집했습니다 .
Aadit M Shah

7

2019 년의 작은 업데이트 :

이제 Array.from은 보편적으로 사용 가능한 것으로 보이며 더 나아가 두 번째 인수 mapFn을 허용 하므로 중간 배열을 만들 수 없습니다. 이것은 기본적으로 다음과 같습니다

Array.from(myMap.entries(), entry => {...});

에 대한 답변이 Array.from이미 존재하기 때문에이 답변에 대한 의견이 있거나 수정을 요청한 것이 더 적합합니다 ...하지만 감사합니다!
Stefano

1

iterables에 대해 배열과 유사한 메소드를 구현하는 https://www.npmjs.com/package/itiriri 와 같은 라이브러리를 사용할 수 있습니다 .

import { query } from 'itiriri';

const map = new Map();
map.set(1, 'Alice');
map.set(2, 'Bob');

const result = query(map)
  .filter([k, v] => v.indexOf('A') >= 0)
  .map([k, v] => `k - ${v.toUpperCase()}`);

for (const r of result) {
  console.log(r); // prints: 1 - ALICE
}

:-) 곧 그것을 시도 할 것이다, 덕분에 너무 많이를 쓰기 위해 -이 lib 디렉토리는 반복 가능 객체 @dimadeveatii로 이동 놀라운 및 누락 된 도관 보인다
안젤로스 Pikoulas

0

배열 배열 (키 및 값)을 얻을 수 있습니다 .

[...this.state.selected.entries()]
/**
*(2) [Array(2), Array(2)]
*0: (2) [2, true]
*1: (2) [3, true]
*length: 2
*/

그런 다음지도 반복자가있는 키와 같이 내부에서 값을 쉽게 얻을 수 있습니다.

[...this.state.selected[asd].entries()].map(e=>e[0])
//(2) [2, 3]

0

fluent-iterable 을 사용 하여 배열로 변환 할 수도 있습니다 .

const iterable: Iterable<T> = ...;
const arr: T[] = fluent(iterable).toArray();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.