Javascript相当于Python的zip函数

pq.*_*pq. 191 javascript python transpose functional-programming

有没有相当于Python的zip函数的javascript?也就是说,给定多个相等长度的数组会创建一对数组.

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

var array1 = [1, 2, 3];
var array2 = ['a','b','c'];
var array3 = [4, 5, 6];
Run Code Online (Sandbox Code Playgroud)

输出数组应该是:

var output array:[[1,'a',4], [2,'b',5], [3,'c',6]]
Run Code Online (Sandbox Code Playgroud)

nin*_*cko 161

2016年更新:

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

zip= rows=>rows[0].map((_,c)=>rows.map(row=>row[c]))
Run Code Online (Sandbox Code Playgroud)

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

> zip([['row0col0', 'row0col1', 'row0col2'],
       ['row1col0', 'row1col1', 'row1col2']]);
[["row0col0","row1col0"],
 ["row0col1","row1col1"],
 ["row0col2","row1col2"]]
Run Code Online (Sandbox Code Playgroud)

(而且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
Run Code Online (Sandbox Code Playgroud)

(请注意,...语法此时可能存在性能问题,并且可能在将来,因此如果您使用可变参数的第二个答案,您可能需要对其进行性能测试.)


这是一个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(...)
Run Code Online (Sandbox Code Playgroud)

以上假设阵列的大小相同,应该是这样.它还假设你传入一个列表参数列表,不像Python的版本,其中参数列表是可变参数.如果您想要所有这些 "功能",请参阅下文.它只需要大约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()
// []
Run Code Online (Sandbox Code Playgroud)

这将模仿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()
// []
Run Code Online (Sandbox Code Playgroud)

如果你使用这最后两个版本(variadic aka.多参数版本),那么zip不再是它自己的逆.要模仿zip(*[...])Python中的习惯用法,zip.apply(this, [...])当你想要反转zip函数或者你想同样拥有可变数量的列表作为输入时,你需要做.


附录:

要使其处理任何可迭代(例如,在Python中,您可以zip在字符串,范围,映射对象等上使用),您可以定义以下内容:

function iterView(iterable) {
    // returns an array equivalent to the iterable
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您zip按以下方式编写,即使这样也没有必要:

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

演示:

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

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

  • 可变参数和任何可迭代的ES6:`zip =(... rows)=> [... rows [0]] .map((_,c)=> rows.map(row => row [c] ));` (6认同)

Bra*_*don 33

看看Underscore图书馆.

Underscore提供了100多个功能,支持你最喜欢的workaday功能助手:map,filter,invoke - 以及更专业的东西:功能绑定,javascript模板,创建快速索引,深度相等测试等等.

- 说出制作它的人

我最近开始专门为它使用它zip(),它留下了很好的第一印象.我正在使用jQuery和CoffeeScript,它与它们完美配合.Underscore会在他们离开的地方捡到,到目前为止它还没有让我失望.哦顺便说一句,它只有3kb缩小.

看看这个.

  • 请尝试以下方法,而不是下划线:http://lodash.com/ - 直接替换,相同的味道,更多的功能,更多的跨浏览器一致性,更好的性能.有关说明,请参见http://kitcambridge.be/blog/say-hello-to-lo-dash/. (11认同)
  • 使用Underscore时,您会感觉更接近Haskell的清晰度和逻辑性. (3认同)

Lor*_*uer 15

除了ninjagecko的优秀而全面的答案之外,将两个JS阵列压缩成"元组模拟"所需的全部内容是:

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

说明:
由于Javascript没有tuples类型,因此元组,列表和集合的函数在语言规范中不是高优先级.
否则,可以通过JS> 1.6中的Array map以直接的方式访问类似的行为.(map实际上通常由JS引擎制造商在许多> JS 1.4引擎中实现,尽管未指定).
主要的区别Python的zip,izip......从结果map的实用的风格,因为map需要一个功能参数.此外,它是Array-instance的一个功能.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]]})
Run Code Online (Sandbox Code Playgroud)

结果:

//'('+_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)
Run Code Online (Sandbox Code Playgroud)

相关表现:

使用mapfor-loops:

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

拉链测试

注意:基本类型例如falseundefined不具有原型对象层次结构,因此不公开toString函数.因此,这些在输出中显示为空.
由于parseInt第二个参数是基数/数字基数,要将数字转换为,并且由于map将索引作为第二个参数传递给其参数 - 函数,因此使用包装函数.


Key*_*van 9

与其他类似Python的函数一起,pythonic提供了一个zip函数,它具有返回惰性求值的额外好处Iterator,类似于其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))];
Run Code Online (Sandbox Code Playgroud)

披露我是Pythonic的作者和维护者


小智 6

具有生成器的现代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 )
    }
}
Run Code Online (Sandbox Code Playgroud)

首先,我们得到一个可迭代的列表iterators。这通常是透明的,但是在这里我们明确地做到这一点,因为我们一步一步地产生了结果,直到其中之一用尽。我们检查.some()给定数组中的任何结果(使用该方法)是否耗尽,如果是,则中断while循环。


Bha*_*tel 6

您可以使用 ES6 制作实用程序功能。

console.json = obj => console.log(JSON.stringify(obj));

const zip = (arr, ...arrs) =>
  arr.map((val, i) => arrs.reduce((a, arr) => [...a, arr[i]], [val]));

// Example

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

console.json(zip(array1, array2));         // [[1,"a"],[2,"b"],[3,"c"]]
console.json(zip(array1, array2, array3)); // [[1,"a",4],[2,"b",5],[3,"c",6]]
Run Code Online (Sandbox Code Playgroud)

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

这是您可以更好地控制它的解决方案。这有点复杂,但值得。

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 ]]
Run Code Online (Sandbox Code Playgroud)


PAD*_*MKO 5

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])));
};
Run Code Online (Sandbox Code Playgroud)

结果:

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

[[1,667,111,11]]

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

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

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

[]

在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
        )
    );
};
Run Code Online (Sandbox Code Playgroud)

结果:

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

[[
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],
    []
));
Run Code Online (Sandbox Code Playgroud)

[[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],
    []
));
Run Code Online (Sandbox Code Playgroud)

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


Amb*_*ber 3

未内置于 Javascript 本身。一些常见的 Javascript 框架(例如 Prototype)提供了实现,或者您可以编写自己的实现。

  • jQuery:http://plugins.jquery.com/project/zip 原型:http://www.prototypejs.org/api/enumerable/zip (2认同)
  • 但请注意,jQuery 的行为与 Python 的行为略有不同,因为它返回一个对象,而不是数组......因此不能将两个以上的列表压缩在一起。 (2认同)