Javascript 相当于 Python 的 zip 函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4856717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Javascript equivalent of Python's zip function
提问by pq.
Is there a javascript equivalent of Python's zip function? That is, given multiple arrays of equal lengths create an array of pairs.
是否有与 Python 的 zip 函数等效的 javascript?也就是说,给定多个长度相等的数组,创建一个成对数组。
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];
The output array should be:
输出数组应该是:
var output array:[[1,'a',4], [2,'b',5], [3,'c',6]]
回答by Amber
Not built-in to Javascript itself. Some of the common Javascript frameworks (such as Prototype) provide an implementation, or you can write your own.
不是内置于 Javascript 本身。一些常见的 Javascript 框架(例如 Prototype)提供了一个实现,或者您可以编写自己的实现。
回答by Keith
The Mochikitlibrary provides this and many other Python-like functions. developer of Mochikit is also a Python fan, so it has the general style of Python, and also the wraps the async calls in a twisted-like framework.
该Mochikit库提供本产品和许多其他Python相似的功能。Mochikit 的开发者也是 Python 爱好者,所以它具有 Python 的一般风格,并且将异步调用包装在一个类似 Twisted 的框架中。
回答by Keith
I took a run at this in pure JS wondering how the plugins posted above got the job done. Here's my result. I'll preface this by saying that I have no idea how stable this will be in IE and the like. It's just a quick mockup.
我在纯 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;
}
It's not bulletproof, but it's still interesting.
它不是防弹的,但它仍然很有趣。
回答by Brandon
Check out the library Underscore.
查看图书馆Underscore。
Underscore provides over 100 functions that support both your favorite workaday functional helpers: map, filter, invoke — as well as more specialized goodies: function binding, javascript templating, creating quick indexes, deep equality testing, and so on.
Underscore 提供了 100 多个函数,支持您最喜欢的日常功能助手:map、filter、invoke——以及更专业的好东西:函数绑定、javascript 模板、创建快速索引、深度相等性测试等等。
– Say the people who made it
– 说制作它的人
I recently started using it specifically for the zip()function and it has left a great first impression. I am using jQuery and CoffeeScript, and it just goes perfectly with them. Underscore picks up right where they leave off and so far it hasn't let me down. Oh by the way, it's only 3kb minified.
我最近开始专门将它用于该zip()功能,它给人留下了很好的第一印象。我正在使用 jQuery 和 CoffeeScript,它与它们完美搭配。下划线从他们离开的地方开始,到目前为止它并没有让我失望。哦,顺便说一下,它只缩小了 3kb。
Check it out.
一探究竟。
回答by ninjagecko
2016 update:
2016年更新:
Here's a snazzier Ecmascript 6 version:
这是一个更时髦的 Ecmascript 6 版本:
zip= rows=>rows[0].map((_,c)=>rows.map(row=>row[c]))
Illustration equiv. to Python{zip(*args)}:
插图等效。到Python{ zip(*args)}:
> zip([['row0col0', 'row0col1', 'row0col2'],
['row1col0', 'row1col1', 'row1col2']]);
[["row0col0","row1col0"],
["row0col1","row1col1"],
["row0col2","row1col2"]]
(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))
(和FizzyTea指出ES6具有可变参数的参数语法,所以下面的函数定义会像Python,但请参阅下面的免责声明......这会不会是其自身的逆所以zip(zip(x))将不等于x;但正如马特·克拉默指出的zip(...zip(...x))==x(如在常规 python 中zip(*zip(*x))==x))
Alternative definition equiv. to Python{zip}:
替代定义等效。到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.)
(请注意,此时...语法可能存在性能问题,将来也可能存在,因此如果您使用带有可变参数的第二个答案,您可能需要对其进行性能测试。)
Here's a 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(...)
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 版本不同。如果您想要所有这些“功能”,请参见下文。它只需要大约 2 行额外的代码。
The following will mimic Python's zipbehavior on edge cases where the arrays are not of equal size, silently pretending the longer parts of arrays don't exist:
以下将模拟 Pythonzip在数组大小不相等的边缘情况下的行为,默默地假装数组的较长部分不存在:
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()
// []
This will mimic Python's itertools.zip_longestbehavior, inserting undefinedwhere arrays are not defined:
这将模仿 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()
// []
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.
如果您使用这最后两个版本(可变参数又名多参数版本),则 zip 不再是它自己的逆版本。要模仿zip(*[...])Python 中的习惯用法,zip.apply(this, [...])当您想要反转 zip 函数或者想要类似地将可变数量的列表作为输入时,您需要这样做。
addendum:
附录:
To make this handle any iterable (e.g. in Python you can use zipon strings, ranges, map objects, etc.), you could define the following:
要使此句柄可迭代(例如,在 Python 中,您可以zip在字符串、范围、地图对象等上使用),您可以定义以下内容:
function iterView(iterable) {
// returns an array equivalent to the iterable
}
However if you write zipin the following way, even that won't be necessary:
但是,如果您zip按以下方式编写,则甚至不需要:
function zip(arrays) {
return Array.apply(null,Array(arrays[0].length)).map(function(_,i){
return arrays.map(function(array){return array[i]})
});
}
Demo:
演示:
> JSON.stringify( zip(['abcde',[1,2,3,4,5]]) )
[["a",1],["b",2],["c",3],["d",4],["e",5]]
(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.)
(或者,range(...)如果您已经编写了一个 Python 风格的函数,您可以使用它。最终您将能够使用 ECMAScript 数组推导式或生成器。)
回答by Pat
Like @Brandon, I recommend Underscore's zipfunction. However, it acts like zip_longest, appending undefinedvalues as needed to return something the length of the longest input.
像@Brandon,我建议下划线的拉链功能。但是,它的作用类似于zip_longest,undefined根据需要附加值以返回最长输入长度的内容。
I used the mixinmethod to extend underscore with a zipShortest, which acts like Python's zip, based off of the library's own source for zip.
我使用该mixin方法用 a 扩展下划线zipShortest,它的作用类似于 Python 的zip,基于库自己的zip.
You can add the following to your common JS code and then call it as if it were part of underscore: _.zipShortest([1,2,3], ['a'])returns [[1, 'a']], for example.
例如,您可以将以下内容添加到您的公共 JS 代码中,然后将其作为下划线的一部分进行调用:_.zipShortest([1,2,3], ['a'])returns [[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;
}});
回答by Lorenz Lo Sauer
In addition to ninjagecko's excellent and comprehensive answer, all it takes to zip two JS-arrays into a "tuple-mimic" is:
除了 ninjagecko 出色而全面的答案之外,将两个 JS 数组压缩为“元组模拟”所需的一切是:
//Arrays: aIn, aOut
Array.prototype.map.call( aIn, function(e,i){return [e, aOut[i]];})
Explanation:
Since Javascript doesn't have a tuplestype, functions for tuples, lists and sets wasn't a high priority in the language specification.
Otherwise, similar behavior is accessible in a straightforward manner via Array map in JS >1.6. (mapis actually often implemented by JS engine makers in many >JS 1.4 engines, despite not specified).
The major difference to Python's zip, izip,... results from map's functional style, since maprequires a function-argument. Additionally it is a function of the Array-instance. One may use Array.prototype.mapinstead, if an extra declaration for the input is an issue.
说明:
由于 Javascript 没有tuples类型,元组、列表和集合的函数在语言规范中不是一个高优先级。
否则,可以通过JS >1.6 中的 Array 映射以直接的方式访问类似的行为。(map实际上通常由 JS 引擎制造商在许多 > JS 1.4 引擎中实现,尽管没有指定)。
与 Python 的zip, izip,...的主要区别来自于map的函数式风格,因为它map需要一个函数参数。此外,它是Array-instance的函数。Array.prototype.map如果输入的额外声明是一个问题,则可以改用。
Example:
例子:
_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]]})
Result:
结果:
//'('+_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)
Related Performance:
相关表现:
Using mapover for-loops:
使用map过for循环:
See: What is the most efficient way of merging [1,2] and [7,8] into [[1,7], [2,8]]
请参阅:将 [1,2] 和 [7,8] 合并为 [[1,7], [2,8]] 的最有效方法是什么


Note:the base types such as falseand undefineddo not posess a prototypal object-hierarchy and thus do not expose a toStringfunction. Hence these are shown as empty in the output.
As parseInt's second argument is the base/number radix, to which to convert the number to, and since mappasses the index as the second argument to its argument-function, a wrapper function is used.
注意:诸如false和 之类的基本类型undefined不构成原型对象层次结构,因此不公开toString函数。因此,这些在输出中显示为空。
AsparseInt的第二个参数是基数/数字基数,要将数字转换为该基数,并且由于map将索引作为第二个参数传递给其参数函数,因此使用了包装函数。
回答by PADYMKO
The Python has two functions: zip and itertools.zip_longest. Implementation on JS/ES6 is like this:
Python 有两个函数:zip 和 itertools.zip_longest。在 JS/ES6 上的实现是这样的:
Implementation Python`s zip on 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])));
};
Results:
结果:
console.log(zip(
[1, 2, 3, 'a'],
[667, false, -378, '337'],
[111],
[11, 221]
));
[ [ 1, 667, 111, 11 ] ]
[ [ 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 ] ]
[ [ 1, 667, 111 ], [ 2, false, 212 ], [ 3, -378, 323 ], [ 'a', '337', 433 ] ]
console.log(zip(
[1, 2, 3, 'a'],
[667, false, -378, '337'],
[111],
[]
));
[]
[]
Implementation Python`s zip_longest on JS/ES6
在 JS/ES6 上实现 Python 的 zip_longest
(https://docs.python.org/3.5/library/itertools.html?highlight=zip_longest#itertools.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
)
);
};
Results:
结果:
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 ] ]
[ [ 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 ] ]
[ [ 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, 'Is None' ], [ 2, false, 'Is None', 'Is None' ],
[ 3, -378, 'Is None', 'Is None' ], [ 'a', '337', 'Is None', 'Is None' ] ]
[ [ 1, 667, 111, 'Is None' ], [ 2, false, 'Is None', 'Is None'],
[ 3, -378, 'Is None', 'Is None' ], [ 'a '、'337'、'无'、'无']]
回答by Dimitris
Modern ES6 example with a generator:
带有生成器的现代 ES6 示例:
function *zip (...iterables){
let iterators = iterables.map(i => i[Symbol.iterator]() )
while (true) {
let results = iterators.map(iter => iter.next() )
if (results.some(res => res.done) ) return
else yield results.map(res => res.value )
}
}
First, we get a list of iterables as iterators. This usually happens transparently, but here we do it explicitly, as we yield step-by-step until one of them is exhausted. We check if any of results (using the .some()method) in the given array is exhausted, and if so, we break the while loop.
首先,我们得到一个可迭代列表作为iterators。这通常透明地发生,但在这里我们明确地这样做,因为我们一步一步地产生,直到其中一个被耗尽。我们检查.some()给定数组中的任何结果(使用方法)是否已用尽,如果是,我们中断 while 循环。
回答by Steven Kalt
This shaves a line off Ddi's iterator-based answer:
function* zip(...toZip) {
const iterators = toZip.map((arg) => arg[Symbol.iterator]());
const next = () => toZip = iterators.map((iter) => iter.next());
while (next().every((item) => !item.done)) {
yield toZip.map((item) => item.value);
}
}

