[javascript] Javascript 반복자를 배열로 변환

최신 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에 대한 의존성은 나를 미치게 합니다 …



답변

임의의 이터 러블을 배열 인스턴스로 변환 하는 새로운 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;
}


답변

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

매우 쉽습니다.

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


답변

을 변환 할 필요가 없습니다 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>

희망이 도움이됩니다.


답변

2019 년의 작은 업데이트 :

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

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


답변

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
}


답변

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

[...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]


답변

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

const iterable: Iterable<T> = ...;
const arr: T[] = fluent(iterable).toArray();