261

I have 2 arrays:

var a = [1, 2, 3]
var b = [a, b, c]

What I want to get as a result is:

[[1, a], [2, b], [3, c]]

It seems simple but I just can't figure out.

I want the result to be one array with each of the elements from the two arrays zipped together.

4
  • 3
    Note that array map() is not supported in IE8, if that is a problem. Polyfill here developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Commented Feb 25, 2014 at 13:37
  • 21
    This question is NOT a duplicate as it only asks for 2 arrays to be zipped instead of N arrays. Thus it is a special case for which there are specialized, more performant solutions. Commented Jun 4, 2016 at 0:52
  • Just to say, the zip analogy comes from python Commented Sep 15, 2024 at 8:26
  • 2
    @Matt Is there any source of it? Haskell, Elixir, Scala, basically most of functional programming languages have had zip() for very long time. I doubt Python is its origin? Commented Feb 26 at 21:46

7 Answers 7

392

Use the map method:

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

var c = a.map(function(e, i) {
  return [e, b[i]];
});

console.log(c)

DEMO

Sign up to request clarification or add additional context in comments.

13 Comments

Total javascript noob here, but I was under the impression that the index has to come before the element? that is, .map(function(index,element))?
@runawaykid The answer is correct. You can see the MDN map documentation on the callback parameter. Params are (in order): currentvalue, index, array
Using the arrow notation is even a bit nicer: var c = a.map((e, i) => [e, b[i]]);
How would you create a dictionary/object from this?
Just note that if the arrays are different lengths, then this will either pad the right side with undefined values (if a is longer) or will omit some entries (if b is longer).
|
161

Zip Arrays of same length:

Using Array.prototype.map()

const zip = (a, b) => a.map((k, i) => [k, b[i]]);

console.log(zip([1,2,3], ["a","b","c"]));
// [[1, "a"], [2, "b"], [3, "c"]]

Zip Arrays of different length:

Using Array.from()

const zip = (a, b) => Array.from(Array(Math.max(b.length, a.length)), (_, i) => [a[i], b[i]]);

console.log( zip([1,2,3], ["a","b","c","d"]) );
// [[1, "a"], [2, "b"], [3, "c"], [undefined, "d"]]

Using Array.prototype.fill() and Array.prototype.map()

const zip = (a, b) => Array(Math.max(b.length, a.length)).fill().map((_,i) => [a[i], b[i]]);

console.log(zip([1,2,3], ["a","b","c","d"]));
// [[1, "a"], [2, "b"], [3, "c"], [undefined, "d"]]

Zip Multiple (n) Arrays:

const zip = (...arr) => Array(Math.max(...arr.map(a => a.length))).fill().map((_,i) => arr.map(a => a[i]));  
console.log(zip([1,2], [3,4], [5,6])); // [[1,3,5], [2,4,6]]

8 Comments

Thanks for this. Foruntanely map worked in my environment nevertheless I've recorded this method just in case.
Elegant, but faulty. Classic zip must be commutative, this implementation is not. check out with arrays of different length.
Thank you @goteguru, I added two more example for array of different length.
Thx @RokoC.Buljan, nice. However I think it should be Math.min. zip comes from the functional languages (just like filter, map, reduce and friends) and it's main purpose is to feeding two streams into one binary computation. The result is undefined anyway if one of them is missing, therefore we should stop early. Moreover, in FP the streams may be infinite (!) which would mean the above algorithm never stops. Of course it's not the case using js Arrays, but still this is the classic "zip". (Performance wise: imagine one array has 100k items, and the other has only twelve).
This is the correct answer to the question, and not the one that OP accepted.
|
11

Zipping by leveraging generator functions

You can also use a generator function to zip().

const a = [1, 2, 3]
const b = ['a', 'b', 'c']

/**
 * Zips any number of arrays. It will always zip() the largest array returning undefined for shorter arrays.
 * @param  {...Array<any>} arrays 
 */
function* zip(...arrays){
  const maxLength = arrays.reduce((max, curIterable) => curIterable.length > max ? curIterable.length: max, 0);
  for (let i = 0; i < maxLength; i++) {
    yield arrays.map(array => array[i]);
  }
}

// put zipped result in an array
const result = [...zip(a, b)]

// or lazy generate the values
for (const [valA, valB] of zip(a, b)) {
  console.log(`${valA}: ${valB}`);  
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

The above works for any number of arrays and will zip() the longest array so undefined is returned as a value for shorter arrays.

Zipping of all Iterables

Here a function which can be used for all Iterables (e.g. Maps, Sets or your custom Iterable), not just arrays.

const a = [1, 2, 3];
const b = ["a", "b", "c"];

/**
 * Zips any number of iterables. It will always zip() the largest Iterable returning undefined for shorter arrays.
 * @param  {...Iterable<any>} iterables
 */
function* zip(...iterables) {
  // get the iterator of for each iterables
  const iters = [...iterables].map((iterable) => iterable[Symbol.iterator]());
  let next = iters.map((iter) => iter.next().value);
  // as long as any of the iterables returns something, yield a value (zip longest)
  while(anyOf(next)) {
    yield next;
    next = iters.map((iter) => iter.next().value);
  }

  function anyOf(arr){
    return arr.some(v => v !== undefined);
  }
}

// put zipped result in aa array
const result = [...zip(a, new Set(b))];

// or lazy generate the values
for (const [valA, valB] of zip(a, new Set(b))) {
  console.log(`${valA}: ${valB}`);
}

Obviously it would also be possible to just use [...Iterable] to transform any Iterable to an array and then use the first function.

Comments

3

Using the reduce method:

const a = [1, 2, 3]
const b = ['a', 'b', 'c']

var c = a.reduce((acc, curr, ind) => {
  acc.push([curr, b[ind]]);
  return acc;
}, []);
console.log(c)

With forEach method:

const a = [1, 2, 3]
const b = ['a', 'b', 'c']
const c = [];
a.forEach((el, ind) => {
    c.push([el, b[ind]])
});
console.log(c)

Comments

1

If you don't mind using lodash, you can use its zip method for this purpose:

import zip from 'lodash/zip' // or require

const a = [1, 2, 3]
const b = ['a', 'b', 'c']

const result = zip(a, b)
// [[1, 'a'], [2, 'b'], [3, 'c']]

Note: You can zip more than 2 arrays.

If you need a custom operation on the elements, you can use zipWith.

Comments

0

Providing a solution with imperative programming by a simple for loop.

This performs better when doing the zip operation on huge data sets compared to the convenient array functions like map() and forEach().

Example:

const a = [1, 2, 3];
const b = ['a', 'b', 'c'];
const result = [];
for (let i = 0; i < a.length; i++) {
  result.push([a[i], b[i]]);
}
console.log(result);

And if you want a 1 line simpler solution then you can use a library like ramda which has a zip function.

Example:

const a = [1, 2, 3];
const b = ['a', 'b', 'c'];
const result = R.zip(a, b);
console.log(result);

Comments

-1

Zip function for 2D arrays

Here is a function that zips multiple arrays. However, It only works with 2D arrays only.

function zip(listOfLists) {
  return listOfLists[0].map((value, index) => {
      let result = [];

      for(let i=0; i <= listOfLists.length-1 ; i++){
          result[i] = listOfLists[i][index]
      }
      return result;
  });
}

This is how you run it.

console.log(
  zip(
    [[1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]]
  )
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.