Javascript equivalent of Python's zip function
Asked Answered
L

24

311

Is there a javascript equivalent of Python's zip function? That is, given multiple arrays of equal lengths create an array of pairs.

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 outputArray = [[1,'a',4], [2,'b',5], [3,'c',6]]
Loftis answered 31/1, 2011 at 22:8 Comment(6)
Is it fair to say that we Python programmers are 'afraid' of dumb methods involving loops because they're slow, and hence always look for built-in methods of doing things. But that in Javascript we should just get on with it and write our loops because they aren't particularly slow?Redeeming
@Redeeming A loop is a loop, hidden behind a 'fast' method or not. JavaScript has definitely been getting more support for higher order functions, with the introduction of Array's forEach, reduce, map, every, etc. It was just the case that zip didn't "make the cut" (a flatMap is also absent), not for performance considerations - but to be fair, .NET (3.5) didn't have a Zip on Enumerable for a couple years! Any 'functionalish' library like underscore/lodash (lodash 3.x has lazy sequence evaluation) will provide an equivalent zip function.Adonic
@Adonic An interpreted loop (such as in Python) will always be much slower than a machine code loop. A JIT-compiled loop (such as in modern JS engines) may approach the native CPU speed, so much that the gain introduced by using a machine code loop may be offset by the overhead of the anonymous function call. Still, it makes sense to have these builtin functions and to profile several variations of your "inner loops" with several JS engines. The results may not be obvious.Acicular
Essentially the same as Transposing a 2D-array in JavaScript.Kosey
@Redeeming I don't want to waste my time writing dumb loops, when I could have a simple function doing the stuff for me. Following your logic, the standard lib would be empty.Vitrics
zip(a,b) isn't a magic Python function, it is a very common and simple functional programming operation often used for co-iterating over multiple lists.Clerkly
S
248

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

Spatter answered 23/4, 2012 at 15:58 Comment(9)
This does not work for me: TypeError: Object 1 has no method 'map'Usa
And ES6 for variadic args and any iterable: zip = (...rows) => [...rows[0]].map((_,c) => rows.map(row => row[c]));Advowson
the "Object 1 has no method 'map'" probably is a case of trying to use this on an object that doesn't have a map method (such as a nodelist, or a string) which was covered in the addendum of this postSpatter
While it's true that the variadic ES6 version doesn't preserve zip(zip(x)) = x, you can still bask in the confidence that zip(...zip(...x)) = x.Safety
const the_longest_array_length = Math.max(...(arrays.map(array => array.length)));Writ
This took me a bit longer to parse, I personally found this easier zip: rows => rows[0].map((_, i) => rows.map(row => row[i])) [i for the index parameter. Great answer though, thanksAnyone
@PaulTorrington: isn't that just the same thing with c renamed to i?Spatter
Yes it is, I often see the callback as (c, i, a) => for current, index, array. Seeing the c confused me into thinking it was the current value not the index, I am sure most people would be smarter but a few might make the same mistake so I hoped my comment might save them some time. The answer is great I have used it in my recent project thanksAnyone
See, simple! This IS why it is nice when these things make it into the language. It's a simple useful abstraction, that makes it more evident what the "intent" is, when people see it in code. Being in the language means... documented and tested. Versus code copied from SO... Still this a good comprehensive answer.Inspan
F
48

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]]
Frobisher answered 31/8, 2011 at 16:53 Comment(2)
Instead of underscore, try this: lodash.com - drop-in replacement, same great flavor, more features, more cross-browser consistency, better perf. See kitcambridge.be/blog/say-hello-to-lo-dash for a description.Knurled
Lodash zip flattens everything into a single arrayToback
R
33

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.

Rom answered 17/1, 2018 at 4:23 Comment(4)
Seems like this runs multiple iterations over results. Once for results.some() and once for results.map().Petiole
@Petiole , results is just an N-tuple where N is the number of iterators to be zipped. It's a very small number, most usually just 2!Rom
I think my comment was a mistake. I implemented this in TypeScript but I think I accepted an Iterable of Iterables. In that case, I needed to store the projected Iterators as an array. Otherwise, if the initial outer argument was e.g., a generator, it would already be exhausted and unable to restart. Since you're using spread notation over the arguments, you should already have a mapped array.Petiole
Isn't there a way to just try/catch the error that would occur when you try to get the next value of an iterator that's done? That would obviate the need to check every iterator every iterationOrmiston
V
19

In addition to ninjagecko's excellent and comprehensive answer, all it takes to zip two JS-arrays into a "tuple-mimic" is:

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

Explanation:
Since Javascript doesn't have a tuples type, 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. (map is 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 map requires a function-argument. Additionally it is a function of the Array-instance. One may use Array.prototype.map instead, if an extra declaration for the input is an issue.

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 map over for-loops:

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

zip tests

Note: the base types such as false and undefined do not posess a prototypal object-hierarchy and thus do not expose a toString function. 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 map passes the index as the second argument to its argument-function, a wrapper function is used.

Voiceless answered 28/7, 2013 at 11:58 Comment(2)
Your first example says "aIn is not a function" when I try it. It works if I call .map from the array instead of as a prototype: aIn.map(function(e, i) {return [e, aOut[i]];}) What is wrong?Cyclotron
@Noumenon, Array.prototype.map should've been Array.prototype.map.call, fixed the answer.Fachini
R
17

Along other Python-like functions, pythonic offers a zip function, with the extra benefit of returning a lazy evaluated Iterator, similar to the behaviour of its 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))];

Disclosure I'm author and maintainer of Pythonic

Rabelaisian answered 7/4, 2018 at 9:34 Comment(0)
C
13

Python has two function to zip sequences: zip and itertools.zip_longest. An implementation in Javascript for the same functionality is this:

Implementation of Python`s zip on JS/ES6

const zip = (...arrays) => {
    const length = Math.min(...arrays.map(arr => arr.length));
    return Array.from({ length }, (value, index) => arrays.map((array => array[index])));
};

Results in:

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

[]

Implementation of Python`s zip_longest on JS/ES6

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

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

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

Chara answered 30/12, 2016 at 13:50 Comment(1)
Only thing I'd change here is that instead of using Array.map I'd use a generator because Array.map will automatically allocate memory for the new array, whereas, using a generator, you only generate one object in memory at a time.Carboloy
H
8

You can make utility function by using 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]]

However, in above solution length of the first array defines the length of the output array.

Here is the solution in which you have more control over it. It's bit complex but worth it.

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 ]]
Highpressure answered 12/4, 2019 at 9:13 Comment(1)
Very clean solution.Cacique
O
4

1. Npm Module: zip-array

I found an npm module that can be used as a javascript version of python zip:

zip-array - A javascript equivalent of Python's zip function. Merges together the values of each of the arrays.

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

2. tf.data.zip() in Tensorflow.js

Another alternate choice is for Tensorflow.js users: if you need a zip function in python to work with tensorflow datasets in Javascript, you can use tf.data.zip() in Tensorflow.js.

tf.data.zip() in Tensorflow.js documented at here

Ophidian answered 6/3, 2019 at 8:7 Comment(0)
P
4

Original answer (see update below)

I modified flm's nifty answer to take an arbitrary number of arrays:

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

Updated answer

As pointed out by Tom Pohl this function can't deal with arrays with falsy values in. Here is an updated/improved version that can deal with any types and also unequal length arrays:

 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]))
Pinball answered 7/11, 2020 at 17:43 Comment(0)
M
3

Not built-in to Javascript itself. Some of the common Javascript frameworks (such as Prototype) provide an implementation, or you can write your own.

Merideth answered 31/1, 2011 at 22:11 Comment(4)
Link? Also, I'd be more interested if jQuery did it, since that's what I'm using...Loftis
jQuery: plugins.jquery.com/project/zip Prototype: prototypejs.org/api/enumerable/zipMerideth
Do note however that the jQuery one behaves slightly differently than the Python one, in that it returns an object, not an array... and thus cannot zip more than 2 lists together.Merideth
Right, the author shouldn't call the jQuery one an equivalent.Loftis
P
3

Like @Brandon, I recommend Underscore's zip function. However, it acts like zip_longest, appending undefined values as needed to return something the length of the longest input.

I used the mixin method to extend underscore with a zipShortest, which acts like Python's zip, based off of the library's own source for 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.

// 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;
}});
Pericynthion answered 29/11, 2012 at 3:34 Comment(1)
Downvote without a comment? I'm happy to improve this answer, but can't without feedback.Pericynthion
R
3

A variation of the 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())

And this is the python's classic "n-group" idiom zip(*[iter(a)]*n):

triples = [...zip(...Array(3).fill(iter(a)))]
Rundgren answered 28/2, 2018 at 17:8 Comment(0)
L
3

You could reduce the array of arrays and map new array by taking the result of the index of the inner array.

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

Fun with spread.

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);
Loess answered 19/10, 2018 at 20:29 Comment(0)
O
3

ES2020 shortest variant:

function * zip(arr1, arr2, i = 0) {
  while(arr1[i] || arr2[i]) yield [arr1[i], arr2[i++]].filter(x => !!x);
}
    
[ ...zip(arr1, arr2) ]  // result
Onitaonlooker answered 5/7, 2020 at 12:43 Comment(2)
nice! I tweaked it a bitPinball
What happens when arr1[i] and arr2[i] are both zero? console.log(...zip([9,0], [2,0])) gives [9,2] instead of [9,2],[0,0]Paloma
C
2

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.

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.

Cassondra answered 31/1, 2011 at 22:46 Comment(4)
jsfiddle looks nice. Has a TidyUp button! The Run button didn't show your console.log output in the Result panel. Why?Loftis
It (console.log) requires something like Firebug to run. Just switch console.log to alert.Cassondra
What's the Result pane for then?Loftis
It shows the HTML of the fiddle. In this case, I'm just doing straight JS. Here's the result using document.write() jsfiddle.net/PyTWw/5Cassondra
S
2

A generator approach to pythons zip function.

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)
Swetlana answered 24/4, 2021 at 21:5 Comment(0)
P
2

I'm not a javascript guy but I feel like many of these answers are trying to find the cutest and most clever solution using Array.map which is fine, but for someone like me that doesn't use javascript every day here are some alternatives that might possibly be a bit more readable.

Maybe a way to avoid some cute and clever code would be:

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
}

If you like generators:

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

Or if you really want to use 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]])
}

As I said, I'm not an everyday javascript guy so, these aren't going to be the most elegant solutions but they are readable to me.

Paloma answered 7/10, 2021 at 21:24 Comment(0)
S
1

I have created a simple function to do so with a option to provide an zipper function

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

Searching answered 22/1, 2021 at 12:43 Comment(0)
G
1

Below is a fast and efficient way of doing this, using iter-ops library, operator 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 ]

The library processes all inputs as iterables, so they are iterated over just once. And it can handle, in the same way, all types of iterable objects - Iterable, AsyncIterable, Iterator, AsyncIterator.


P.S. I'm the author of iter-ops.

Gwendolyn answered 19/12, 2021 at 16:46 Comment(2)
Why is the pipe needed, would it be possible to have just zip(array1, array2, array3)? Nice library BTW!.Accelerant
@Accelerant In that library, all operators are designed to work inside the pipeline, provided by pipe function. It is however possible to execute an operator outside of pipeline, as zip(array2, array3)(array1), but it is not a common practice, and pipe provides strong-type casting, which doesn't work outside the pipeline.Gwendolyn
E
1

Here is my solution

let zip = (a, b) => (a.length < b.length
  ? a.map((e, i) => [e, b[i]])
  : b.map((e, i) => [a[i], e]))
Elocution answered 23/4, 2022 at 5:32 Comment(0)
U
0

The Mochikit library 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.

Uterine answered 31/1, 2011 at 22:25 Comment(0)
S
0

There is no equivalent function. If you have only a few arrays you should use a for loop to get an index and then use the index to access the arrays:

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

You can have an inner loop over the arrays if you have more.

Solis answered 22/1, 2021 at 15:43 Comment(0)
I
-1

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);
  }
}
Information answered 20/2, 2018 at 21:55 Comment(0)
P
-1

If you are fine with ES6:

const zip = (arr,...arrs) =>(
                            arr.map(
                              (v,i) => arrs.reduce((a,arr)=>[...a, arr[i]], [v])))
Pernambuco answered 25/4, 2018 at 16:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.