Javascript的等效Python的zip函数

是否有一个Python的zipfunction的JavaScript等价物? 也就是说,给定两个长度相等的数组创build一个数组对。

例如,如果我有三个看起来像这样的数组:

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版本:

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

插图:

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

(和FizzyTea指出,ES6具有可变参数语法,所以下面将像python行事,但见下面免责声明…这不会是它自己的逆,所以zip(zip(x))将不等于x 🙂

 > 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 

(请注意, ...语法在这个时候可能会有性能问题,并且可能在将来会有问题,所以如果您使用可变参数使用第二个答案,则可能需要进行性能testing。


这是一个oneliner:

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

上面假定arrays的大小是相同的。 它也假设你传入一个列表参数的列表,不像Python的版本是参数列表是可变的。 如果你想要所有这些 “function”,请参阅下文。 它只需要约2个额外的代码行。

下面将模仿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数组的地方插入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() // [] 

如果你使用最后两个版本(variadic又名多参数版本),那么zip不再是它自己的反面。 为了模仿Python中的zip(*[...])成语,你需要做zip.apply(this, [...])来反转zip函数或者想要类似的variables列表的数量作为input。


附录

为了使这个句柄成为可迭代的(例如在Python中,你可以在string,范围,地图对象等上使用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]] 

(或者你可以使用range(...) Python风格的函数,如果你已经写了一个函数,最终你将能够使用ECMAScript数组理解或生成器。)

检查图书馆下划线 。

Underscore提供了超过100个函数,支持你最喜欢的workadayfunction助手:地图,filter,调用 – 以及更专业的好东西:函数绑定,JavaScript模板,创build快速索引,深度平等testing等等。

– 说出来的人

我最近开始专门为zip()函数使用它,并留下了很好的第一印象。 我正在使用jQuery和CoffeeScript,它只是完美的与他们。 下划线在他们离开的地方拾取,到目前为止它还没有让我失望。 哦,顺便说一下,它只有3kb的缩小。

一探究竟。

除了ninjagecko的优秀和全面的答案,只需要将两个JS数组压缩成一个“元组模拟”就是:

 //Arrays: aIn, aOut Array.prototype.map.call( aIn, function(e,i){return [e, aOut[i]];}) 

说明:
由于Javascript没有tuplestypes,元组,列表和集合的函数在语言规范中并不是高优先级。
否则,通过JS> 1.6中的数组映射可以直接访问类似的行为。 ( map实际上通常由JS引擎制造商在许多> JS 1.4引擎中实现,尽pipe没有指定)。
Python的zipizip ,…与map的函数风格有很大的不同,因为map需要一个函数参数。 另外它是Array -instance的一个函数。 如果input的额外声明是一个问题,可以使用Array.prototype.map

例:

 _tarrin = [0..constructor, function(){}, false, undefined, '', 100, 123.324, 2343243243242343242354365476453654625345345, 'sdf23423dsfsdf', 'sdf2324.234dfs','234,234fsf','100,100','100.100'] _parseInt = function(i){return parseInt(i);} _tarrout = _tarrin.map(_parseInt) _tarrin.map(function(e,i,a){return [e, _tarrout[i]]}) 

结果:

 //'('+_tarrin.map(function(e,i,a){return [e, _tarrout[i]]}).join('),\n(')+')' >> (function Number() { [native code] },NaN), (function (){},NaN), (false,NaN), (,NaN), (,NaN), (100,100), (123.324,123), (2.3432432432423434e+42,2), (sdf23423dsfsdf,NaN), (sdf2324.234dfs,NaN), (234,234fsf,234), (100,100,100), (100.100,100) 

相关performance:

map for循环:

请参阅: 将[1,2]和[7,8]合并为[[1,7],[2,8]]的最有效方式是什么

邮编测试

注意:诸如falseundefined类的基本types不构成原型对象层次结构,因此不公开toString函数。 因此,这些在输出中显示为空。
因为parseInt的第二个参数是基数/数字基数,要将数字转换为数字,并且由于map将索引作为第二个parameter passing给它的参数函数,所以使用包装函数。

没有内置到Javascript本身。 一些常用的Javascript框架(如Prototype)提供了一个实现,或者你可以自己编写。

Python有两个函数:zip和itertools.zip_longest。 JS / ES6的实现是这样的:

在JS / ES6上实现Python的zip

 const zip = (...arrays) => { const length = Math.min(...arrays.map(arr => arr.length)); return Array.from({ length }, (value, index) => arrays.map((array => array[index]))); }; 

结果:

 console.log(zip( [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [11, 221] )); 

[[1,667,111,11]]

 console.log(zip( [1, 2, 3, 'a'], [667, false, -378, '337'], [111, 212, 323, 433, '1111'] )); 

[[1,667,111],[2,false,212],[3,-378,323],['a','337',433]]

 console.log(zip( [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [] )); 

[]

在JS / ES6上实现Python的zip_longest

https://docs.python.org/3.5/library/itertools.html?highlight=zip_longest#itertools.zip_longest

 const zipLongest = (placeholder = undefined, ...arrays) => { const length = Math.max(...arrays.map(arr => arr.length)); return Array.from( { length }, (value, index) => arrays.map( array => array.length - 1 >= index ? array[index] : placeholder ) ); }; 

结果:

 console.log(zipLongest( undefined, [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [] )); 

[[1,667,111,undefined],[2,false,undefined,undefined],
[3,-378,undefined,undefined],['a','337',undefined,undefined]]

 console.log(zipLongest( null, [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [] )); 

[[1,667,111,null],[2,false,null,null],[3,-378,null,null],['a','337',null,null]]

 console.log(zipLongest( 'Is None', [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [] )); 

[[1,667,111,'是'],[2,虚假,'无','无'],
[3,-378,'无','无'],['a','337','无','无']]

Mochikit库提供了这个和许多其他类似Python的函数。 Mochikit的开发者也是一个Python迷,所以它具有Python的一般风格,并且将asynchronous调用包装在扭曲的框架中。

像@Brandon一样,我推荐Underscore的zipfunction。 然而,它的行为像zip_longest ,根据需要附加undefined值,以返回最长input的长度。

我使用mixin方法来扩展下划线,使用一个zipShortest ,它的行为就像Python的zip ,基于图书馆自己的zip源代码 。

您可以将以下内容添加到您的常用JS代码,然后将其作为下划线的一部分进行调用: _.zipShortest([1,2,3], ['a'])返回[[1, 'a']] , 例如。

 // Underscore library addition - zip like python does, dominated by the shortest list // The default injects undefineds to match the length of the longest list. _.mixin({ zipShortest : function() { var args = Array.Prototype.slice.call(arguments); var length = _.min(_.pluck(args, 'length')); // changed max to min var results = new Array(length); for (var i = 0; i < length; i++) { results[i] = _.pluck(args, "" + i); } return results; }}); 

我在这个纯粹的JS上试了一下,想知道上面发布的插件如何完成这个工作。 这是我的结果。 我将在这之前说,我不知道这将是多么稳定在IE等。 这只是一个快速的模型。

 init(); function init() { var one = [0, 1, 2, 3]; var two = [4, 5, 6, 7]; var three = [8, 9, 10, 11, 12]; var four = zip(one, two, one); //returns array //four = zip(one, two, three); //returns false since three.length !== two.length console.log(four); } function zip() { for (var i = 0; i < arguments.length; i++) { if (!arguments[i].length || !arguments.toString()) { return false; } if (i >= 1) { if (arguments[i].length !== arguments[i - 1].length) { return false; } } } var zipped = []; for (var j = 0; j < arguments[0].length; j++) { var toBeZipped = []; for (var k = 0; k < arguments.length; k++) { toBeZipped.push(arguments[k][j]); } zipped.push(toBeZipped); } return zipped; }