Javascript equivalent of Python's zip function Javascript equivalent of Python's zip function python python

Javascript equivalent of Python's zip function


2016 update:

Here's a snazzier Ecmascript 6 version:

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

Illustration equiv. to 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))

Alternative definition equiv. to 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. That said it's been quite a while since it's been in the standard.)

Make sure to note the addendum if you wish to use this on strings (perhaps there's a better way to do it now with es6 iterables).


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.

The following will mimic Python's zip behavior on edge cases where the arrays are not of equal size, silently pretending the longer parts of arrays don't exist:

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_longest behavior, inserting undefined where arrays are not defined:

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.


addendum:

To make this handle any iterable (e.g. in Python you can use zip on strings, ranges, map objects, etc.), you could define the following:

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

However if you write zip in the following way, even that won't be necessary:

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


Check out the library 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.

– 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.

Check it out:

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


Modern ES6 example with a generator:

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.