Clojure exercise to transpose three lists
Table of Contents
The need: take multiple vectors and view each vector as a column (not a row) of the data, so all the first items go together, all the second, etc. Here’s a rough version that felt too clunky (alert: it features a number) and fragile but happens to demonstrate the needs:
(let [[names jobs langs :as all] [["john" "jane" "michael"]
["chef" "driver" "vet"]
["English" "German" "French"]]]
(map #(zipmap [:name :job :lang] %)
(partition 3
(interleave names jobs langs))))
;; ({:name "john", :job "chef", :lang "English"}
;; {:name "jane", :job "driver", :lang "German"}
;; {:name "michael", :job "vet", :lang "French"})
Solution
(let [[names jobs langs :as all] [["john" "jane" "michael"]
["chef" "driver" "vet"]
["English" "German" "French"]]]
(apply map #(zipmap [:name :job :lang] %&) all))
;; => ({:name "john", :job "chef", :lang "English"} {:name "jane", :job "driver", :lang "German"} {:name "michael", :job "vet", :lang "French"})
This solution taught me two new things:
- that
map
and sozipmap
can take any number of args, plucking the nth item of each collection arg and feeding it to the original function (first) arg. - In lambda functions,
%&
can be used to insert all args in a collection
Resources
- Conversation: https://clojureverse.org/t/most-graceful-way-for-a-lists-maps-transposition/6677
- Map (hence zipmap) is polyvariadic: https://clojuredocs.org/clojure.core/map and https://clojuredocs.org/clojure.core/zipmap
- “rest args” for inline lambda syntax: https://clojure.org/guides/learn/functions#_anonymous_function_syntax