Using 'map' with collections of different sizes in clojure

I would like to understand the idiomatic way of working with collections of different sizes in clojure. Is there a way I can tell the 'map' function to put the rest of the collection with some default value?

As an example, suppose I have 3 vectors:

(def x [1 2 3 4]) (def y [1 2 3 4 5]) (def z [1 2 3 4 5 6 7]) (map + xyz) ; yields (3 6 9 12) 

In this case, how can I stuff x and y with zeros and have this output:

 (3 6 9 12 10 6 7) 
+7
collections functional-programming clojure map
source share
2 answers

map does not do this itself, but you can use a combination of concat and repeat to get the desired result:

 (def x [1 2 3 4]) (def y [1 2 3 4 5]) (def z [1 2 3 4 5 6 7]) (map + (concat x (repeat 0)) (concat y (repeat 0)) z) ; => (3 6 9 12 10 6 7) 

Here's the API documentation for concat , and for repeat .

And here is a sketch of how you can get a little distracted from this, so you do not need to know which of the collections is the longest. (In the above snippet, if you concat all collections to (repeat 0) , you will have an infinite sequence).

 (defn map-longest [f default & colls] (lazy-seq (when (some seq colls) (cons (apply f (map #(if (seq %) (first %) default) colls)) (apply map-longest f default (map rest colls)))))) (map-longest + 0 [1 2 3 4] [1 2 3 4 5] [1 2 3 4 5 6 7]) ; => (3 6 9 12 10 6 7) 

You can see a couple of other approaches as answers to this previous stack overflow question .

+12
source share

You can combine a variable-length map vector with the following function:

 (defn merge-maps [& args] (let [max-count (apply max (map #(count %1) args)) items (map #(take max-count (concat %1 (repeat nil))) args)] (apply map merge items))) 

This function makes the solution more generalized and can take any length of the map vector.

0
source share

All Articles