将Javascript迭代器转换为数组

Ste*_*ano 147 javascript ecmascript-6

我正在尝试使用Javascript EC6中的新Map对象,因为它已经在最新的Firefox和Chrome版本中得到支持.

但我发现它在"功能"编程中非常有限,因为它缺乏经典的地图,过滤器等方法,可以很好地配合一[key, value]对.它有一个forEach但不返回回调结果.

如果我可以将它map.entries()从MapIterator转换为一个简单的数组,那么我可以使用标准.map,.filter没有额外的黑客攻击.

将Javascript迭代器转换为数组是否有"好"的方法?在python中它就像做一样简单list(iterator)...但是Array(m.entries())返回一个以Iterator为第一个元素的数组!

编辑

我忘了指定我正在寻找一个适用于地图工作的答案,这意味着至少Chrome和Firefox(Array.from在Chrome中不起作用).

PS.

我知道有很棒的wu.js,但它对traceur的依赖让我失望......

Ber*_*rgi 211

您正在寻找将任意迭代转换为数组实例的新Array.from函数:

var arr = Array.from(map.entries());
Run Code Online (Sandbox Code Playgroud)

它现在支持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;
}
Run Code Online (Sandbox Code Playgroud)

  • @AaditMShah:迭代器的关键是什么?当然,如果你迭代一个地图,你可以定义`yourTransformation = function([key,value],index){...}` (3认同)

Gin*_*den 36

[...map.entries()] 要么 Array.from(map.entries())

这非常容易.

无论如何 - 迭代器缺少reduce,filter和类似的方法.你必须自己编写它们,因为它比将Map转换为数组和返回更具有性能.但是不要做跳转Map - > Array - > Map - > Array - > Map - > Array,因为它会杀死性能.

  • 而且,正如我在原始问题中所写,`[iterator]`不起作用,因为在Chrome中它创建了一个带有单个`iterator`元素的数组,并且`[... map.entries()]`不是Chrome中接受的语法 (2认同)
  • Chrome现在已接受@Stefano [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#compat-desktop)的语法 (2认同)

Aad*_*hah 13

没有必要将a变换MapArray.您可以简单地为对象创建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;
}
Run Code Online (Sandbox Code Playgroud)

例如,您可以将一个bang(即!字符)附加到其键为基元的映射的每个条目的值.

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";
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

您还可以添加mapfilter方法,Map.prototype以使其更好地阅读.尽管通常不建议以修改本机的原型但我认为,一个例外可能的情况下进行map,并filterMap.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";
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


编辑:在Bergi的回答中,他为所有可迭代对象创建了泛型mapfilter生成器函数.使用它们的优点是,由于它们是生成器函数,因此它们不分配中间可迭代对象.

例如,我mapfilter上面定义的函数创建了新Map对象.因此调用object.filter(primitive).map(appendBang)会创建两个新Map对象:

var intermediate = object.filter(primitive);
var result = intermediate.map(appendBang);
Run Code Online (Sandbox Code Playgroud)

创建中间可迭代对象非常昂贵.Bergi的生成器功能解决了这个问题.它们不分配中间对象,但允许一个迭代器将其值懒惰地提供给下一个.这种优化在功能编程语言中称为融合或毁林,它可以显着提高程序性能.

我对Bergi的生成器函数唯一的问题是它们不是特定于Map对象的.相反,它们针对所有可迭代对象进行了推广.因此,它不是用(value, key)对调用回调函数(正如我在a上映射时所期望的那样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;
}
Run Code Online (Sandbox Code Playgroud)

它们可以如下使用:

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";
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

如果你想要一个更流畅的界面,那么你可以做这样的事情:

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";
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

希望有所帮助.


nro*_*niv 8

2019 年的一个小更新:

现在 Array.from 似乎是普遍可用的,而且,它接受第二个参数mapFn,这可以防止它创建一个中间数组。这基本上是这样的:

Array.from(myMap.entries(), entry => {...});
Run Code Online (Sandbox Code Playgroud)

  • 由于“Array.from”的答案已经存在,这更适合作为对该答案的评论或请求编辑......但是谢谢! (2认同)

Val*_*Rob 8

您可以获取数组的数组(键和值):

\n\n
[...this.state.selected.entries()]\n/**\n*(2) [Array(2), Array(2)]\n*0: (2) [2, true]\n*1: (2) [3, true]\n*length: 2\n*/\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,您可以轻松地从内部获取值,例如带有地图迭代器的键。

\n\n
[...this.state.selected[asd].entries()].map(e=>e[0])\n//(2)\xc2\xa0[2, 3]\n
Run Code Online (Sandbox Code Playgroud)\n