javascript中是否有类似于Python的zip函数?也就是说,给定多个相等长度的数组,创建一个由对组成的数组。

例如,如果我有三个这样的数组:

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

输出数组应该是:

var outputArray = [[1,'a',4], [2,'b',5], [3,'c',6]]

当前回答

惰性生成器解决方案的一个变体:

function* iter(it) { yield* it; } function* zip(...its) { its = its.map(iter); while (true) { let rs = its.map(it => it.next()); if (rs.some(r => r.done)) return; yield rs.map(r => r.value); } } for (let r of zip([1,2,3], [4,5,6,7], [8,9,0,11,22])) console.log(r.join()) // the only change for "longest" is some -> every function* zipLongest(...its) { its = its.map(iter); while (true) { let rs = its.map(it => it.next()); if (rs.every(r => r.done)) return; yield rs.map(r => r.value); } } for (let r of zipLongest([1,2,3], [4,5,6,7], [8,9,0,11,22])) console.log(r.join())

这是python经典的“n-group”习语zip(*[iter(a)]*n):

triples = [...zip(...Array(3).fill(iter(a)))]

其他回答

1. 模块:zip-array

我发现了一个npm模块,可以用作python zip的javascript版本:

zip-array——javascript中类似于Python的zip函数。将每个数组的值合并在一起。

https://www.npmjs.com/package/zip-array

2. Tensorflow.js中的tf.data.zip()

tensorflow .js用户的另一个替代选择是:如果你需要python中的zip函数来处理Javascript中的tensorflow数据集,你可以使用tensorflow .js中的tf.data.zip()。

Tensorflow.js中的tf.data.zip()文档在这里

原始答案(见下文更新)

我修改了flm的漂亮答案,以获取任意数量的数组:

函数* zip(数组,I = 0) { 虽然(我< Math.min(…arrays.map(({长度})= >长度))){ 收益率数组。Map ((arr, j) => arr[j <数组。长度- 1 ?I: i++]) } }

更新后的答案

正如Tom Pohl所指出的,这个函数不能处理数组中有假值的数组。下面是一个更新/改进的版本,可以处理任何类型和长度不等的数组:

函数* zip(数组,I = 0) { 虽然(我< Math.min(…arrays.map (arr = > arr.length))) { 收益率数组。Map ((arr, j) => arr[j <数组。长度- 1 ?I: i++]) } } Const arr1 = [false,0,1,2] Const arr2 = [100,null,99,98,97] Const arr3 = [7,8,undefined,"monkey","banana"] console.log(…zip ([arr1、arr2 arr3)))

你可以使用ES6来创建实用函数。

控制台。json = obj => console.log(json .stringify(obj)); Const zip = (arr,…arrs) => 加勒比海盗。Map ((val, i) => arrs。Reduce ((a, arr) =>[…]A, arr[i]], [val])); / /实例 Const array1 = [1,2,3]; Const array2 = ['a','b','c']; Const array3 = [4,5,6]; 控制台。json (zip (array1 array2));/ /[[1, "一个"],[2,“b”],[3,“c”]] 控制台。Json (zip(array1, array2, array3));/ /[[1”“4],[2“b”5],[3“c”6]]

但是,在上述解决方案中,第一个数组的长度定义了输出数组的长度。

这里有一个解决方案,你可以更好地控制它。这有点复杂,但值得。

function _zip(func, args) { const iterators = args.map(arr => arr[Symbol.iterator]()); let iterateInstances = iterators.map((i) => i.next()); ret = [] while(iterateInstances[func](it => !it.done)) { ret.push(iterateInstances.map(it => it.value)); iterateInstances = iterators.map((i) => i.next()); } return ret; } const array1 = [1, 2, 3]; const array2 = ['a','b','c']; const array3 = [4, 5, 6]; const zipShort = (...args) => _zip('every', args); const zipLong = (...args) => _zip('some', args); console.log(zipShort(array1, array2, array3)) // [[1, 'a', 4], [2, 'b', 5], [3, 'c', 6]] console.log(zipLong([1,2,3], [4,5,6, 7])) // [ // [ 1, 4 ], // [ 2, 5 ], // [ 3, 6 ], // [ undefined, 7 ]]

2016年更新:

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

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)))

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

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

如果你想在字符串上使用它,一定要注意它的附录(也许现在用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(...)

上面假设数组的大小相等,因为它们应该是相等的。它还假设你传递了一个单独的list of lists参数,不像Python版本的参数列表是可变的。如果你想要所有这些“特性”,请参见下面。它只需要额外的两行代码。

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

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:

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()
// []

如果你使用这最后两个版本(可变变量aka。多参数版本),那么zip就不再是它自己的逆。要模仿Python中的zip(*[…])习语,需要执行zip。当您想要反转zip函数,或者如果您想类似地使用可变数量的列表作为输入时,应用(this,[…])。


附录:

要使此句柄为任何可迭代对象(例如,在Python中,您可以对字符串、范围、映射对象等使用zip),您可以定义以下内容:

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

然而,如果你以下面的方式写zip,即使这样也不需要:

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风格的函数(如果你已经写过的话)。最终,您将能够使用ECMAScript数组推导式或生成器。)

ES2020最短变体:

function * zip(arr1, arr2, i = 0) {
  while(arr1[i] || arr2[i]) yield [arr1[i], arr2[i++]].filter(x => !!x);
}
    
[ ...zip(arr1, arr2) ]  // result