【问题标题】:Javascript equivalent of Python's zip functionJavascript 等效于 Python 的 zip 函数
【发布时间】:2011-06-18 21:47:45
【问题描述】:

是否有与 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]]

【问题讨论】:

  • 公平地说,我们 Python 程序员“害怕”涉及循环的愚蠢方法,因为它们很慢,因此总是寻找内置的做事方法。但是在 Javascript 中我们应该继续使用它并编写我们的循环,因为它们不是特别慢?
  • @LondonRob 循环是一个循环,无论是否隐藏在“快速”方法后面。随着 Array 的 forEach、reduce、map、every 等的引入,JavaScript 无疑已经获得了对高阶函数的更多支持。只是 zip 没有“使cut”(flatMap 也缺席),不是出于性能考虑 - 但公平地说,.NET (3.5) 在 Enumerable 上几年没有 Zip!任何像 underscore/lodash(lodash 3.x 具有惰性序列评估)这样的“功能性”库都将提供等效的 zip 函数。
  • @user2864740 解释循环(例如在 Python 中)总是比机器代码循环慢很多。 JIT 编译的循环(例如在现代 JS 引擎中)可能会接近本机 CPU 速度,以至于使用机器代码循环引入的增益可能会被匿名函数调用的开销所抵消。尽管如此,拥有这些内置函数并使用多个 JS 引擎来分析“内部循环”的几个变体仍然是有意义的。结果可能并不明显。

标签: javascript python functional-programming transpose


【解决方案1】:

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

【讨论】:

  • 链接?另外,如果 jQuery 做到了,我会更感兴趣,因为这就是我正在使用的......
  • 请注意,jQuery 的行为与 Python 的略有不同,因为它返回一个对象,而不是数组...因此不能将超过 2 个列表压缩在一起。
  • 对,作者不应该将 jQuery 称为等价物。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • jsfiddle 看起来不错。有一个整理按钮! “运行”按钮未在“结果”面板中显示您的 console.log 输出。为什么?
    • 它 (console.log) 需要类似 Firebug 的东西才能运行。只需将console.log 切换为alert。
    • 那么结果窗格是做什么用的?
    • 它显示了小提琴的 HTML。在这种情况下,我只是在做直接的 JS。这是使用document.write()jsfiddle.net/PyTWw/5的结果
    【解决方案4】:

    查看库Underscore。

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

    – 说制作它的人

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

    检查一下:

    _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
    // returns [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
    

    【讨论】:

    • 使用 Underscore 时,您会感觉更接近 Haskell 的清晰性和逻辑性。
    • 而不是下划线,试试这个:lodash.com - 插入式替换,同样很棒的味道,更多的功能,更多的跨浏览器一致性,更好的性能。有关说明,请参阅 kitcambridge.be/blog/say-hello-to-lo-dash。
    【解决方案5】:

    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 迭代有更好的方法)。


    这是一个单线:

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

    以上假设数组大小相同,应该是这样。它还假设您传入一个列表参数列表,这与参数列表是可变参数的 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()
    // []
    

    这将模仿 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()
    // []
    

    如果您使用这最后两个版本(variadic aka.multi-argument 版本),那么 zip 不再是它自己的逆。为了模仿 Python 中的 zip(*[...]) 习语,当您想要反转 zip 函数或想要类似地使用可变数量的列表作为输入时,您需要执行 zip.apply(this, [...])。


    附录:

    要使此句柄可迭代(例如,在 Python 中,您可以在字符串、范围、地图对象等上使用 zip),您可以定义以下内容:

    function iterView(iterable) {
        // returns an array equivalent to the iterable
    }
    

    但是,如果您将zip 写在下面的way 中,那么即使这样也没有必要:

    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 数组解析或生成器。)

    【讨论】:

    • 这对我不起作用:TypeError: Object 1 has no method 'map'
    • 和 ES6 用于可变参数和任何可迭代:zip = (...rows) =&gt; [...rows[0]].map((_,c) =&gt; rows.map(row =&gt; row[c]));
    • “对象 1 没有方法 'map'”可能是尝试在没有映射方法(例如节点列表或字符串)的对象上使用它的情况已包含在这篇文章的附录中
    • 虽然可变参数 ES6 版本确实没有保留 zip(zip(x)) = x,但您仍然可以对 zip(...zip(...x)) = x 充满信心。
    • const the_longest_array_length = Math.max(...(arrays.map(array =&gt; array.length)));
    【解决方案6】:

    和@Brandon 一样,我推荐Underscore 的zip 函数。但是,它的作用类似于zip_longest,根据需要附加undefined 值以返回最长输入的长度。

    我使用mixin 方法用zipShortest 扩展下划线,其作用类似于Python 的zip,基于the library's own source for 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;
    }});
    

    【讨论】:

    • 不发表评论就投反对票?我很高兴改进这个答案,但不能没有反馈。
    【解决方案7】:

    除了 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 引擎制造商在许多 >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]]})
    

    结果:

    //'('+_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)
    

    相关表现:

    在for-loops 上使用map:

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

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

    【讨论】:

    • 您的第一个示例在我尝试时说“aIn 不是函数”。如果我从数组中调用 .map 而不是原型,它会起作用:aIn.map(function(e, i) {return [e, aOut[i]];}) 出了什么问题?
    • @Noumenon, Array.prototype.map 应该是Array.prototype.map.call,修正了答案。
    【解决方案8】:

    Python 有两个压缩序列的函数:zip 和 itertools.zip_longest。相同功能的 Javascript 实现如下:

    在 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,未定义,未定义],['a','337',未定义, 未定义]]

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

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

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

    [ [ [ 1, 667, 111, '没有'], [ 2, false, '没有', '没有'],
    [ 3, -378, '没有', '没有'], [ 'a', '337', '没有', '是 无']]

    【讨论】:

    • 我在这里唯一要改变的是,我不使用Array.map,而是使用生成器,因为Array.map 会自动为新数组分配内存,而使用生成器时,您只需一次在内存中生成一个对象。
    【解决方案9】:

    带有生成器的现代 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循环。

    【讨论】:

    • 这个答案可能需要更多解释。
    • 我们从可迭代对象中得到一个迭代器列表。这通常是透明地发生的,这里我们明确地这样做,因为我们一步一步地让步,直到其中一个用尽。检查数组中的任何一个(.some() 方法)是否耗尽,如果是,我们中断。
    【解决方案10】:

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

    【讨论】:

      【解决方案11】:

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

      【讨论】:

      • 我想知道这个有什么问题,我写的完全一样。对我来说,这感觉比其他所有人都好,但也许我们都错了......我一直在寻找一种方法来为其添加流类型,但我正在苦苦挣扎:D。
      【解决方案12】:

      与其他类似 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))];
      

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

      【讨论】:

        【解决方案13】:

        如果你对 ES6 没问题:

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

        【讨论】:

          【解决方案14】:

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

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

          传播的乐趣。

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

          【讨论】:

            【解决方案15】:

            1。 Npm 模块:zip-array

            我找到了一个npm模块,可以用作python的javascript版本zip:

            zip-array - 相当于 Python 的 zip 函数的 JavaScript。将每个数组的值合并在一起。

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

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

            另一个替代选择是 Tensorflow.js 用户:如果您需要 Python 中的 zip 函数来处理 Javascript 中的 tensorflow 数据集,您可以在 Tensorflow.js 中使用 tf.data.zip()。

            tf.data.zip() 在 Tensorflow.js 中记录在 here

            【讨论】:

              【解决方案16】:

              你可以使用 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]]

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

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

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

              【讨论】:

              • 非常干净的解决方案。
              【解决方案17】:

              ES2020 最短变体:

              function * zip(arr1, arr2, i = 0) {
                while(arr1[i] || arr2[i]) yield [arr1[i], arr2[i++]].filter(x => !!x);
              }
                  
              [ ...zip(arr1, arr2) ]  // result
              

              【讨论】:

              • 不错!我tweaked it a bit
              • 当arr1[i] 和arr2[i] 都为零时会发生什么? console.log(...zip([9,0], [2,0])) 给出 [9,2] 而不是 [9,2],[0,0]
              【解决方案18】:

              原始答案(见下方更新)

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

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

              更新答案

              正如Tom Pohl 所指出的,此函数无法处理包含虚假值的数组。这是一个更新/改进的版本,可以处理任何类型以及不等长的数组:

               function* zip(arrays, i = 0) {
                    while (i<Math.min(...arrays.map(arr=>arr.length))) {
                      yield arrays.map((arr, j) => arr[j < arrays.length - 1 ? i : i++])
                    }
                   }
                   
                 const arr1 = [false,0,1,2]
              const arr2 = [100,null,99,98,97]
              const arr3 = [7,8,undefined,"monkey","banana"]
              
              console.log(...zip([arr1,arr2,arr3]))

              【讨论】:

                【解决方案19】:

                我创建了一个简单的函数来执行此操作,并提供了一个提供拉链功能的选项

                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

                【讨论】:

                  【解决方案20】:

                  没有等效的功能。如果您只有几个数组,您应该使用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]);
                  }
                  

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

                  【讨论】:

                    【解决方案21】:

                    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)
                    

                    【讨论】:

                      【解决方案22】:

                      我不是 javascript 人,但我觉得其中许多答案都在尝试使用 Array.map 找到最可爱和最聪明的解决方案,这很好,但对于像我这样不每天都使用 javascript 的人来说是一些替代方案,可能可能更具可读性。

                      也许避免一些可爱和聪明代码的方法是:

                      function zip(a,b){
                          // pre-allocate an array to hold the results 
                          rval=Array(Math.max(a.length, b.length));
                          for(i=0; i<rval.length; i++){ 
                              rval[i]=[a[i],b[i]] 
                          }
                          return rval
                      }
                      

                      如果你喜欢生成器:

                      function* _zip(a,b){
                          len = Math.max(a.length, b.length) // handle different sized arrays
                          for(i=0; i<len; i++) { yield [a[i],b[i]] }
                      }
                      

                      或者如果你真的想使用Array.map:

                      function map(a,b){
                          x = a.length > b.length ? a : b // call map on the biggest array
                          return x.map((_,i)=>[a[i],b[i]])
                      }
                      

                      正如我所说,我不是一个日常使用 javascript 的人,所以这些不会是最优雅的解决方案,但它们对我来说是可读的。

                      【讨论】:

                        【解决方案23】:

                        下面是一种快速有效的方法,使用iter-ops库,运算符zip:

                        const {pipe, zip} = require('iter-ops');
                        
                        const i = pipe(array1, zip(array2, array3));
                        
                        console.log(...i); //=> [ 1, 'a', 4 ] [ 2, 'b', 5 ] [ 3, 'c', 6 ]
                        

                        库将所有输入作为可迭代对象处理,因此它们只被迭代一次。它可以以同样的方式处理所有类型的可迭代对象——Iterable、AsyncIterable、Iterator、AsyncIterator。


                        附:我是iter-ops的作者。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2011-06-25
                          • 2010-11-07
                          • 2011-07-09
                          • 1970-01-01
                          • 1970-01-01
                          • 2016-01-08
                          相关资源
                          最近更新 更多