In computer science, zipping is a function which maps a tuple of sequences into a sequence of tuples. This name zip derives from the action of a zipper in that it interleaves two formerly disjoint sequences. The inverse function is unzip.

Example

Given the three words cat, fish and be where |cat| is 3, |fish| is 4 and |be| is 2. Let <math>\ell</math> denote the length of the longest word which is fish; <math>\ell = 4</math>. The zip of cat, fish, be is then 4 tuples of elements:

: <math> (c,f,b)(a,i,e)(t,s,\#)(\#,h,\#)</math>

where # is a symbol not in the original alphabet. In Haskell this truncates to the shortest sequence <math>\underline{\ell}</math>, where <math>\underline{\ell} = 2</math>:

<syntaxhighlight lang="haskell">

zip3 "cat" "fish" "be"

-- [('c','f','b'),('a','i','e')]

</syntaxhighlight>

Definition

Let &Sigma; be an alphabet, # a symbol not in &Sigma;.

Let x<sub>1</sub>x<sub>2</sub>... x<sub>|x|</sub>, y<sub>1</sub>y<sub>2</sub>... y<sub>|y|</sub>, z<sub>1</sub>z<sub>2</sub>... z<sub>|z|</sub>, ... be n words (i.e. finite sequences) of elements of &Sigma;. Let <math>\ell</math> denote the length of the longest word, i.e. the maximum of |x|, |y|, |z|, ... .

The zip of these words is a finite sequence of n-tuples of elements of , i.e. an element of <math>((\Sigma\cup\{\# \})^n)^*</math>:

:<math> (x_1,y_1,\ldots)(x_2,y_2,\ldots)\ldots(x_\ell,y_\ell,\ldots)</math>,

where for any index , the w<sub>i</sub> is #.

The zip of x, y, z, ... is denoted zip(x, y, z, ...) or x ⋆ y ⋆ z ⋆ ...

The inverse to zip is sometimes denoted unzip.

A variation of the zip operation is defined by:

:<math> (x_1,y_1,\ldots)(x_2,y_2,\ldots)\ldots(x_{\underline{\ell,y_{\underline{\ell,\ldots)</math>

where <math>\underline{\ell}</math> is the minimum length of the input words. It avoids the use of an adjoined element <math>\#</math>, but destroys information about elements of the input sequences beyond <math>\underline{\ell}</math>.

In programming languages

Zip functions are often available in programming languages, often referred to as . In Lisp-dialects one can simply the desired function over the desired lists, is variadic in Lisp so it can take an arbitrary number of lists as argument. An example from Clojure:

<syntaxhighlight lang="clojure">

;; `nums' contains an infinite list of numbers (0 1 2 3 ...)

(def nums (range))

(def tens [10 20 30])

(def firstname "Alice")

;; To zip (0 1 2 3 ...) and [10 20 30] into a vector, invoke `map vector' on them; same with list

(map vector nums tens) ; ⇒ ([0 10] [1 20] [2 30])

(map list nums tens) ; ⇒ ((0 10) (1 20) (2 30))

(map str nums tens) ; ⇒ ("010" "120" "230")

;; `map' truncates to the shortest sequence; note missing \c and \e from "Alice"

(map vector nums tens firstname) ; ⇒ ([0 10 \A] [1 20 \l] [2 30 \i])

(map str nums tens firstname) ; ⇒ ("010A" "120l" "230i")

;; To unzip, apply `map vector' or `map list'

(apply map list (map vector nums tens firstname))

;; ⇒ ((0 1 2) (10 20 30) (\A \l \i))

</syntaxhighlight>

In Common Lisp:

<syntaxhighlight lang="lisp">

(defparameter nums '(1 2 3))

(defparameter tens '(10 20 30))

(defparameter firstname "Alice")

(mapcar #'list nums tens)

;; ⇒ ((1 10) (2 20) (3 30))

(mapcar #'list nums tens (coerce firstname 'list))

;; ⇒ ((1 10 #\A) (2 20 #\l) (3 30 #\i)) — truncates on shortest list

;; Unzips

(apply #'mapcar #'list (mapcar #'list nums tens (coerce firstname 'list)))

;; ⇒ ((1 2 3) (10 20 30) (#\A #\l #\i))

</syntaxhighlight>

Languages such as Python provide a function. in conjunction with the operator unzips a list: similarly the functions and are available for unzipping:

<syntaxhighlight lang="haskell">

-- nums contains an infinite list of numbers [1, 2, 3, ...]

nums = [1..]

tens = [10, 20, 30]

firstname = "Alice"

zip nums tens

-- ⇒ [(1,10), (2,20), (3,30)] — zip, truncates infinite list

unzip $ zip nums tens

-- ⇒ ([1,2,3], [10,20,30]) — unzip

zip3 nums tens firstname

-- ⇒ [(1,10,'A'), (2,20,'l'), (3,30,'i')] — zip, truncates

unzip3 $ zip3 nums tens firstname

-- ⇒ ([1,2,3], [10,20,30], "Ali") — unzip

</syntaxhighlight>

Language comparison

List of languages by support of zip:

{| class="wikitable"

|+ Zip in various languages

|-

! scope="col" | Language

! scope="col" | Zip

! scope="col" | Zip 3 lists

! scope="col" | Zip n lists

! scope="col" | Notes

|-

! scope="row" | Chapel

|

|

|

| The shape of each iterator, the rank and the extents in each dimension, must be identical.

|-

! scope="row" | Clojure

| <br/>

| <br/>

| <br/>

| Stops after the length of the shortest list.

|-

! scope="row" | Common Lisp

|

|

|

| Stops after the length of the shortest list.

|-

! scope="row" | D

| <br/>

| <br/>

| <br/>

| The stopping policy defaults to shortest and can be optionally provided as shortest, longest, or requiring the same length. The second form is an example of UFCS.

|-

! scope="row" | F#

| <br/><br/>

| <br/><br/>

|

|

|-

! scope="row" | Haskell

|

|

|

| for n > 3 is available in the module Data.List. Stops after the shortest list ends.

|-

! scope="row" | Python

|

|

|

| and (3.x) stops after the shortest list ends, whereas (2.x) and (3.x) extends the shorter lists with items

|-

! scope="row" | Ruby

|

|

|

| When the list being executed upon (list1) is shorter than the lists being zipped the resulting list is the length of list1. If list1 is longer nil values are used to fill the missing values

|-

! scope="row" | Scala

|

|

|

| If one of the two collections is longer than the other, its remaining elements are ignored.

|}

{| class="wikitable"

|+ Unzip in various languages

|-

! scope="col" | Language

! scope="col" | Unzip

! scope="col" | Unzip 3 tuples

! scope="col" | Unzip n tuples

! scope="col" | Notes

|-

! scope="row" | Clojure

|

|

|

|

|-

! scope="row" | Common Lisp

|

|

|

|

|-

! scope="row" | F#

| <br/><br/>

| <br/><br/>

|

|

|-

! scope="row" | Haskell

|

|

|

| for n > 3 is available in the module

|-

! scope="row" | Python

|

|

|

|

|}

See also

  • Map (higher-order function)

References