Javascript相当于Python的zip函数

时间:2011-01-31 22:08:12

标签: javascript python functional-programming transpose

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

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

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

21 个答案:

答案 0 :(得分:152)

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))将不等于{ {1}};虽然Matt Kramer指出x(就像常规python zip(...zip(...x))==x))

替代定义等同。 Python {zip(*zip(*x))==x}:

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 语法此时可能存在性能问题,可能在将来也有问题,因此如果您使用可变参数的第二个答案,则可能需要对其进行性能测试。)


这是一个oneliner:

...

以上假设阵列大小相同,应该是这样。它还假设你传入一个列表参数列表,不像Python的版本,其中参数列表是可变参数。 如果您想要所有这些“功能”,请参阅下文。它只需要大约2行代码。

以下将模仿Python的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(...) 行为,在数据大小不相同的边缘情况下,默默地假装数组的较长部分不存在:

zip

这将模仿Python的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() // [] 行为,插入itertools.zip_longest未定义数组:

undefined

如果你使用最后两个版本(variadic aka。多参数版本),那么zip不再是它自己的逆。要模仿Python中的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() // [] 成语,当你想要反转zip函数或者你想同样拥有可变数量的列表作为输入时,你需要做zip(*[...])


<强>附录

要使其处理任何可迭代(例如在Python中,您可以对字符串,范围,地图对象等使用zip.apply(this, [...])),您可以定义以下内容:

zip

但是,如果您在以下way中撰写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数组理解或生成器。)

答案 1 :(得分:28)

查看库Underscore

  

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

- 说出制作它的人

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

检查出来。

答案 2 :(得分:14)

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

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

<强>解释
由于Javascript没有tuples类型,因此元组,列表和集合的函数不是语言规范中的高优先级。
否则,可以通过Array map in JS >1.6以直接的方式访问类似的行为。 (map实际上经常由JS引擎制造商在许多&gt; JS 1.4引擎中实现,尽管未指定) Python的zipizip,...的主要区别来自map的函数样式,因为map需要一个函数参数。另外,它是Array - 实例的函数。如果输入的额外声明是个问题,可以使用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)

相关表现:

使用map而不是for - 循环:

请参阅:What is the most efficient way of merging [1,2] and [7,8] into [[1,7], [2,8]]

zip tests

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

答案 3 :(得分:4)

与其他类似Python的函数一样,pythonic提供zip函数,返回延迟评估Iterator的额外好处,类似于Python counterpart的行为:

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

答案 4 :(得分:4)

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

在JS / ES6上实现Python的压缩

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],[&#39; a&#39;,   &#39; 337&#39;,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],[&#39; a&#39;,&#39; 337&#39;,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],[&#39; a&#39;,&#39; 337&#39;,null,null]]

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

[[1,667,111,&#39;没有&#39; ],[2,假,&#39;没有&#39;,&#39;没有&#39; ],
  [3,-378,&#39;没有&#39;,&#39;没有&#39; ],[&#39; a&#39;,&#39; 337&#39;,&#39; Is None&#39;,&#39; Is   无&#39; ]]

答案 5 :(得分:4)

带有生成器的现代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 )
    }
}

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

答案 6 :(得分:3)

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

答案 7 :(得分:2)

ES2020最短变体:

where (user_id || ' ' || user_name || ' ' || user_unit || ' ' ||
       user_last_name || ' ' user_first_name || ' ' || user_email
      ) like '%' || :input_string || '%'

答案 8 :(得分:2)

您可以通过使用ES6来实现实用程序功能。

const zip = (arr, ...arrs) => {
  return 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.log(zip(array1, array2));                  // [[1, 'a'], [2, 'b'], [3, 'c']]
console.log(zip(array1, array2, array3));          // [[1, 'a', 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 ]]

答案 9 :(得分:2)

与@Brandon一样,我推荐Underscorezip功能。但是,它的作用类似于zip_longest,根据需要附加undefined值,以返回最长输入的长度。

我使用mixin方法使用zipShortest扩展下划线,zip就像Python的_.zipShortest([1,2,3], ['a'])一样,基于the library's own source for zip

您可以将以下内容添加到常见的JS代码中,然后将其称为下划线的一部分:[[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; }}); ,例如。

{{1}}

答案 10 :(得分:2)

python zip 函数的生成器方法。

function* zip(...arrs){
  for(let i = 0; i < arrs[0].length; i++){
    a = arrs.map(e=>e[i])
    if(a.indexOf(undefined) == -1 ){yield a }else{return undefined;}
  }
}
// use as multiple iterators
for( let [a,b,c] of zip([1, 2, 3, 4], ['a', 'b', 'c', 'd'], ['hi', 'hello', 'howdy', 'how are you']) )
  console.log(a,b,c)

// creating new array with the combined arrays
let outputArr = []
for( let arr of zip([1, 2, 3, 4], ['a', 'b', 'c', 'd'], ['hi', 'hello', 'howdy', 'how are you']) )
  outputArr.push(arr)

答案 11 :(得分:1)

我创建了一个简单的函数来实现这一点,并提供了一个提供拉链功能的选项

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

答案 12 :(得分:1)

我修改了flm的漂亮answer以采用任意数量的数组:

function* zip(arrays, i = 0) {
  while (arrays[0][i]) {
    yield arrays.map((arr, j) => arr[j < arrays.length - 1 ? i : i++])
  }
 }

答案 13 :(得分:1)

您可以通过获取内部数组的索引结果来减少数组的数组并映射新数组。

var array1 = [1, 2, 3],
    array2 = ['a','b','c'],
    array3 = [4, 5, 6],
    array = [array1, array2, array3],
    transposed = array.reduce((r, a) => a.map((v, i) => (r[i] || []).concat(v)), []);

console.log(transposed);

答案 14 :(得分:1)

1。 Npm模块:zip-array

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

zip-array -与Python的zip函数等效的javascript。将每个数组的值合并在一起。

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

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

Tensorflow.js用户的另一种选择:如果需要python中的zip函数来使用Javascript中的tensorflow数据集,则可以在Tensorflow.js中使用tf.data.zip()

here

中记录的Tensorflow.js中的

tf.data.zip()

答案 15 :(得分:0)

没有等效的功能。如果您只有几个数组,则应使用 for 循环获取索引,然后使用该索引访问数组:

var array1 = [1, 2, 3];
var array2 = ['a','b','c'];

for (let i = 0; i < Math.min(array1.length, array2.length); i++) {
    doStuff(array1[i], array2[i]);
}

如果你有更多的数组,你可以在数组上有一个内部循环。

答案 16 :(得分:0)

lazy generator solution的变体:

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

答案 17 :(得分:0)

我在纯粹的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;
}

它不是防弹的,但它仍然很有趣。

答案 18 :(得分:0)

Mochikit库提供了这个以及许多其他类似Python的函数。 Mochikit的开发人员也是Python的粉丝,因此它具有Python的一般风格,并且还在扭曲的框架中包含异步调用。

答案 19 :(得分:-1)

如果你对ES6很好:

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

答案 20 :(得分:-1)

这会使Ddi基于迭代器的答案消失:

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