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]]
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]]
当前回答
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数组推导式或生成器。)
其他回答
不是Javascript本身内置的。一些常见的Javascript框架(如Prototype)提供了一个实现,或者你也可以自己编写。
与其他类似Python的函数一样,pythonic提供了一个zip函数,其额外的好处是返回一个惰性求值迭代器,类似于Python对应的行为:
import {zip, zipLongest} from 'pythonic';
const arr1 = ['a', 'b'];
const arr2 = ['c', 'd', 'e'];
for (const [first, second] of zip(arr1, arr2))
console.log(`first: ${first}, second: ${second}`);
// first: a, second: c
// first: b, second: d
for (const [first, second] of zipLongest(arr1, arr2))
console.log(`first: ${first}, second: ${second}`);
// first: a, second: c
// first: b, second: d
// first: undefined, second: e
// unzip
const [arrayFirst, arraySecond] = [...zip(...zip(arr1, arr2))];
我是Pythonic的作者和维护者
你可以使用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 ]]
如果你喜欢ES6:
const zip = (arr,...arrs) =>(
arr.map(
(v,i) => arrs.reduce((a,arr)=>[...a, arr[i]], [v])))
我创建了一个简单的函数,通过一个选项来提供一个拉链函数
function zip(zipper, ...arrays) {
if (zipper instanceof Array) {
arrays.unshift(zipper)
zipper = (...elements) => elements
}
const length = Math.min(...arrays.map(array => array.length))
const zipped = []
for (let i = 0; i < length; i++) {
zipped.push(zipper(...arrays.map(array => array[i])))
}
return zipped
}
https://gist.github.com/AmrIKhudair/4b740149c29c492859e00f451832975b