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에는 키 값 쌍이 있습니다. 따라서 겸손한 의견으로 는 반복자에 대한 일반 map및 filter함수 를 정의하는 것은 의미가 없습니다 . 대신, 각 반복 가능한 객체는 자신이 있어야 map하고 filter기능을. 이 의미가 있기 때문에 map및 filter구조 (아마하지 작업을 보존되어 filter있지만, map확실히이다) 따라서 map및 filter기능들이 이상 매핑 또는 필터링하는 것을 반복 가능한 객체의 구조를 알아야한다. 생각해보십시오. 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. 당신은 간단하게 만들 수 map및 filter대한 기능을 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>

당신은 또한 추가 할 수 있습니다 map와 filter의 방법은 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의 대답에서 그는 모든 반복 가능한 객체에 대해 일반 map및 filter생성기 함수를 만들었습니다 . 그것들을 사용하는 장점은 생성기 함수이므로 중간 반복 가능 객체를 할당하지 않는다는 것입니다.

예를 들어, 위에서 정의한 my map및 filter함수는 새 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의 답변은 객체의 키 가 손실 되어 객체 를 변환하는 데 사용할 수없는 모든 반복 가능한 객체 에 일반 함수 map와 filter함수를 사용하는 것을 보여줍니다 . 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.