Javascript 等效于 Python 的 zip 函数

是否有与 Python 的 zip 函数等效的 javascript?也就是说,给定多个长度相等的数组创建一个对数组.

Is there a javascript equivalent of Python's zip function? That is, given multiple arrays of equal lengths create an array of pairs.

例如,如果我有三个如下所示的数组:

For instance, if I have three arrays that look like this:

var array1 = [1, 2, 3];
var array2 = ['a','b','c'];
var array3 = [4, 5, 6];

输出数组应该是:

var output array:[[1,'a',4], [2,'b',5], [3,'c',6]]

推荐答案

2016年更新:

这是一个更时髦的 Ecmascript 6 版本:

Here's a snazzier Ecmascript 6 version:

zip= rows=>rows[0].map((_,c)=>rows.map(row=>row[c]))

插图等价物.Python{zip(*args)}:

> zip([['row0col0', 'row0col1', 'row0col2'],
       ['row1col0', 'row1col1', 'row1col2']]);
[["row0col0","row1col0"],
 ["row0col1","row1col1"],
 ["row0col2","row1col2"]]

(并且 FizzyTea 指出 ES6 具有可变参数语法,因此下面的函数定义将像 python,但请参阅下面的免责声明...这不会是它自己的逆,所以 zip(zip(x)) 将不等于 x;尽管正如 Matt Kramer 指出的那样 zip(...zip(...x))==x (就像在常规python zip(*zip(*x))==x))

(and FizzyTea points out that ES6 has variadic argument syntax, so the following function definition will act like python, but see below for disclaimer... this will not be its own inverse so zip(zip(x)) will not equal x; though as Matt Kramer points out zip(...zip(...x))==x (like in regular python zip(*zip(*x))==x))

替代定义等价.Python{zip}:

> zip = (...rows) => [...rows[0]].map((_,c) => rows.map(row => row[c]))
> zip( ['row0col0', 'row0col1', 'row0col2'] ,
       ['row1col0', 'row1col1', 'row1col2'] );
             // note zip(row0,row1), not zip(matrix)
same answer as above

(请注意,... 语法目前可能存在性能问题,将来也可能存在问题,因此如果您使用带有可变参数的第二个答案,您可能需要执行测试它.也就是说,自从它进入标准以来已经有一段时间了.)

(Do note that the ... syntax may have performance issues at this time, and possibly in the future, so if you use the second answer with variadic arguments, you may want to perf test it. That said it's been quite a while since it's been in the standard.)

如果您希望在字符串上使用它,请务必注意附录(也许现在使用 es6 迭代有更好的方法).

Make sure to note the addendum if you wish to use this on strings (perhaps there's a better way to do it now with es6 iterables).

这是一个单线:

function zip(arrays) {
    return arrays[0].map(function(_,i){
        return arrays.map(function(array){return array[i]})
    });
}

// > zip([[1,2],[11,22],[111,222]])
// [[1,11,111],[2,22,222]]]

// If you believe the following is a valid return value:
//   > zip([])
//   []
// then you can special-case it, or just do
//  return arrays.length==0 ? [] : arrays[0].map(...)


以上假设数组大小相同,应该是这样.它还假设您传入一个列表参数列表,这与参数列表是可变参数的 Python 版本不同.如果您想要所有这些功能",请参见下文.它只需要大约 2 行额外的代码.


The above assumes that the arrays are of equal size, as they should be. It also assumes you pass in a single list of lists argument, unlike Python's version where the argument list is variadic. If you want all of these "features", see below. It takes just about 2 extra lines of code.

以下将在数组大小不相等的边缘情况下模仿 Python 的 zip 行为,默默地假装数组的较长部分不存在:

The following will mimic Python's zip behavior on edge cases where the arrays are not of equal size, silently pretending the longer parts of arrays don't exist:

function zip() {
    var args = [].slice.call(arguments);
    var shortest = args.length==0 ? [] : args.reduce(function(a,b){
        return a.length<b.length ? a : b
    });

    return shortest.map(function(_,i){
        return args.map(function(array){return array[i]})
    });
}

// > zip([1,2],[11,22],[111,222,333])
// [[1,11,111],[2,22,222]]]

// > zip()
// []

这将模仿 Python 的 itertools.zip_longest 行为,在未定义数组的地方插入 undefined:

This will mimic Python's itertools.zip_longest behavior, inserting undefined where arrays are not defined:

function zip() {
    var args = [].slice.call(arguments);
    var longest = args.reduce(function(a,b){
        return a.length>b.length ? a : b
    }, []);

    return longest.map(function(_,i){
        return args.map(function(array){return array[i]})
    });
}

// > zip([1,2],[11,22],[111,222,333])
// [[1,11,111],[2,22,222],[null,null,333]]

// > zip()
// []

如果您使用这最后两个版本(variadic aka.multi-argument 版本),那么 zip 不再是它自己的逆.要模仿 Python 中的 zip(*[...]) 习语,您需要在需要时执行 zip.apply(this, [...])反转 zip 函数,或者如果您希望类似地具有可变数量的列表作为输入.

If you use these last two version (variadic aka. multiple-argument versions), then zip is no longer its own inverse. To mimic the zip(*[...]) idiom from Python, you will need to do zip.apply(this, [...]) when you want to invert the zip function or if you want to similarly have a variable number of lists as input.

附录:

要使这个句柄可迭代(例如,在 Python 中,您可以在字符串、范围、地图对象等上使用 zip),您可以定义以下内容:

To make this handle any iterable (e.g. in Python you can use zip on strings, ranges, map objects, etc.), you could define the following:

function iterView(iterable) {
    // returns an array equivalent to the iterable
}

但是,如果您按照以下 方式 编写 zip,即使这样也不会必要的:

However if you write zip in the following way, even that won't be necessary:

function zip(arrays) {
    return Array.apply(null,Array(arrays[0].length)).map(function(_,i){
        return arrays.map(function(array){return array[i]})
    });
}

演示:

> JSON.stringify( zip(['abcde',[1,2,3,4,5]]) )
[["a",1],["b",2],["c",3],["d",4],["e",5]]

(或者,如果您已经编写了 Python 风格的函数,也可以使用 range(...).最终您将能够使用 ECMAScript 数组解析或生成器.)

(Or you could use a range(...) Python-style function if you've written one already. Eventually you will be able to use ECMAScript array comprehensions or generators.)

相关文章