将Javascript迭代器转换为数组

时间:2015-02-25 12:06:16

标签: javascript ecmascript-6

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

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

如果我可以将其map.entries()从MapIterator转换为简单数组,那么我可以使用标准.map.filter而无需额外的黑客攻击。

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

修改

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

PS。

我知道那里有奇妙的wu.js,但它对traceur的依赖让我失望......

7 个答案:

答案 0 :(得分:179)

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

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

现在是supported in Edge, FF, Chrome and Node 4+

当然,直接在迭代器接口上定义mapfilter和类似方法可能是值得的,这样就可以避免分配数组。您可能还想使用生成器函数而不是高阶函数:

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;
}

答案 1 :(得分:27)

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

这非常容易。

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

答案 2 :(得分:12)

无需将Map转换为Array。您只需为map个对象创建filterMap函数:

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>
&#13;
&#13;
&#13;

您还可以在map上添加filterMap.prototype方法,以便更好地阅读。虽然通常不建议修改原生原型,但我认为对于map filterMap.prototype可能会例外:

&#13;
&#13;
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";
}
&#13;
<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>
&#13;
&#13;
&#13;


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

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

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

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

我对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;
}

可以按如下方式使用:

&#13;
&#13;
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";
}
&#13;
<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>
&#13;
&#13;
&#13;

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

&#13;
&#13;
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";
}
&#13;
<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>
&#13;
&#13;
&#13;

希望有所帮助。

答案 3 :(得分:1)

您可以使用类似https://www.npmjs.com/package/itiriri的库,该库为可迭代对象实现类似数组的方法:

 @GetMapping("/user-profile/{id}")
    public ModelAndView add(@PathVariable String id) {

        return this.view("user-profile");
    }

答案 4 :(得分:1)

2019年的一个小更新:

现在Array.from似乎已普遍可用,而且它接受第二个参数 mapFn ,这阻止了它创建中间数组。基本上看起来像这样:

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

答案 5 :(得分: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]

答案 6 :(得分:0)

您还可以使用fluent-iterable转换为数组:

lemma mul_add (t a b : nat) : t * (a + b) = t * a + t * b :=

begin

  induction b with B hB,

  -- b base case
  rw add_zero a,
  rw mul_zero t,
  rw add_zero (t*a),
  refl,

  rw add_succ a B,
  rw mul_succ t (a + B),
  rw hB,
  rw add_assoc (t*a) (t*B) t,
  rw <- mul_succ t B,
  refl,

end