DEV Community

Cover image for Efficient zip function in JavaScript
Mátyás Fodor
Mátyás Fodor

Posted on

Efficient zip function in JavaScript

Efficient zip function in JavaScript

The Python zip function is a clever analogy of a real zipper: It allows you to iterate over multiple arrays element-wise without having to worry about indices. It is quite handy when you have to manually pair elements coming from two (or more) data sources:

>>> scoreList = [5, 3, 6, 8]
>>> playerList = ['Mary', 'John', 'Emma', 'Gavin']
>>> list(zip(scoreList, playerList))
# [(5, 'Mary'), (3, 'John'), (6, 'Emma'), (8, 'Gavin')]
Enter fullscreen mode Exit fullscreen mode

Please note the list() call is needed to convert the result to a list, otherwise it returns something like

>>> zip(scoreList, playerList)
# <zip object at 0x109b19d00>
Enter fullscreen mode Exit fullscreen mode

which is an object implementing the iterator interface. Why the hassle with this object? Why is it not just a list? The iterator interface allows lazy-evaluation, meaning it won't create a list, only if it's asked. This object instead allows generating elements on-demand, which can be more efficient. Let's say I want to find the first player with score 6:

for score, player in zip(scoreList, playerList):
  if score == 6:
    print(f'player {player} has score 6')
    break
Enter fullscreen mode Exit fullscreen mode

In this case, the iteration stops with ((6, 'Emma')) and the last pair ((8, 'Gavin')) would never be constructed.

The implementation is quite interesting, because zip not only works with two lists, it can more than two arrays as input parameters. This can be particularly useful when trying to transpose a list of lists (a matrix). If you're not familiar with the term transpose, it simply means a list of list is simply flipped by its diagonal. The transposition of

[
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]
Enter fullscreen mode Exit fullscreen mode

is

>>> list(zip(*[
...  [1, 2, 3],
...  [4, 5, 6],
...  [7, 8, 9]
... ]))
[
  [1, 4, 7],
  [2, 5, 8],
  [3, 6, 9]
]
Enter fullscreen mode Exit fullscreen mode

What happens if the input arrays do not have the same length? According to the Python docs it stops when the shortest input iterable is exhausted, meaning it will return as many elements as long the shortest array is:

>>> list(zip([1, 2, 3], [4, 5]))
[(1, 4), (2, 5)]
Enter fullscreen mode Exit fullscreen mode

If all elements need to be returned, itertools.zip_longest should be used.

And just to spice things up the little, let me mention that as the zip function expects iterables as input, not specifically arrays. Iterables are more generic than arrays, without going too much into the details, they are objects that can be iterated over one element at the time, and they can signal if they are exhausted (iteration finished). In practice, it means that any object that can be iterated over can be used as inputs of zip: tuples, sets, dictionaries, range, or even results of other zips. Mind-blowing, right? 🤯 It is also possible to create an infinite zip. Let me use itertools.count to demonstrate it. It is very similar to range() except it has no stopping criteria, so if it is used in a for loop it keeps yielding values unless it is stopped.

>>> for a, b in zip(itertools.count(start=0, step=2), itertools.count(start=1, step=2)):
...     print(a, b)
1 2
3 4
5 6
...
Enter fullscreen mode Exit fullscreen mode

I really hope I could convince you by now, how cool and versatile this Python standard library function is. Why can't we have nice things in JavaScript? Well we can, you just probably end up hunting for third-parties on npm or ready-made solutions on Stack Overflow. But is there anything more satisfying than using your home-grown utilities? You can find it out on my website.

Latest comments (0)