147

In Clojure, I want to combine two lists to give a list of pairs,

> (zip '(1 2 3) '(4 5 6))  
((1 4) (2 5) (3 6))

In Haskell or Ruby the function is called zip. Implementing it is not difficult, but I wanted to make sure I wasn't missing a function in Core or Contrib.

There is a zip namespace in Core, but it is described as providing access to the Zipper functional technique, which does not appear to be what I am after.

Is there an equivalent function for combining 2 or more lists, in this way, in Core?

If there is not, is it because there is an idiomatic approach that renders the function unneeded?

John Kane
  • 3,601
  • 5
  • 23
  • 18

7 Answers7

246
(map vector '(1 2 3) '(4 5 6))

does what you want:

=> ([1 4] [2 5] [3 6])

Haskell needs a collection of zipWith (zipWith3, zipWith4, ...) functions, because they all need to be of a specific type; in particular, the number of input lists they accept needs to be fixed. (The zip, zip2, zip3, ... family can be regarded as a specialisation of the zipWith family for the common use case of tupling).

In contrast, Clojure and other Lisps have good support for variable arity functions; map is one of them and can be used for "tupling" in a manner similar to Haskell's

zipWith (\x y -> (x, y))

The idiomatic way to build a "tuple" in Clojure is to construct a short vector, as displayed above.

(Just for completeness, note that Haskell with some basic extensions does allow variable arity functions; using them requires a good understanding of the language, though, and the vanilla Haskell 98 probably doesn't support them at all, thus fixed arity functions are preferrable for the standard library.)

Dave Liepmann
  • 1,555
  • 1
  • 18
  • 22
Michał Marczyk
  • 83,634
  • 13
  • 201
  • 212
  • 11
    Note that this behaves differently than `zip` when the collections are not the same length. Ruby will continue processing and supply `nil`s for the shorter collection, whereas Clojure will stop processing once one of the collections is exhausted. – Nate W. Jun 17 '14 at 18:45
  • @NateW. That is good to note, thanks. In Haskell `zip` behaves like Clojure's `map` in this respect. – Michał Marczyk Jun 17 '14 at 21:30
28
(partition 2 (interleave '(1 2 3) '(4 5 6))) 
=> ((1 4) (2 5) (3 6))

or more generally

(defn zip [& colls]
  (partition (count colls) (apply interleave colls)))

(zip '( 1 2 3) '(4 5 6))           ;=> ((1 4) (2 5) (3 6))

(zip '( 1 2 3) '(4 5 6) '(2 4 8))  ;=> ((1 4 2) (2 5 4) (3 6 8))
Dave Liepmann
  • 1,555
  • 1
  • 18
  • 22
Lambder
  • 2,953
  • 1
  • 26
  • 20
15
(map vector [1 2 3] [4 5 6])
danlei
  • 14,121
  • 5
  • 58
  • 82
12

to give you exactly what you wanted, mapping list across the two lists will give you a list of lists like in your example. I think that many Clojurians would tend to use vectors for this though it will work with anything. and the inputs do not need to be the same type. map creates seqs from them and then maps the seqs so any seq'able input will work fine.

(map list '(1 2 3) '(4 5 6))
(map list  [1 2 3] '(4 5 6))
(map hash-map  '(1 2 3) '(4 5 6))
(map hash-set  '(1 2 3) '(4 5 6))
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
3

The built-in way would simply be the function 'interleave':

(interleave [1 2 3 4] [5 6 7 8]) => [1 5 2 6 3 7 4 8]
lsh
  • 658
  • 5
  • 12
-1

There is a function called zipmap, that may have the similar effect, (zipmap (1 2 3)(4 5 6)) The ouput is as fllows: {3 6, 2 5, 1 4}

Panic Fred
  • 44
  • 2
-4

#(apply map list %) transposes a matrix just like the Python zip* function. As a macro definition:

user=> (defmacro py-zip [lst] `(apply map list ~lst))

#'user/py-zip

user=> (py-zip '((1 2 3 4) (9 9 9 9) (5 6 7 8)))

((1 9 5) (2 9 6) (3 9 7) (4 9 8))

user=> (py-zip '((1 9 5) (2 9 6) (3 9 7) (4 9 8)))

((1 2 3 4) (9 9 9 9) (5 6 7 8))