Here’s how you can zip up multiple lists into a single list of pairs or tuples using (map vector )
.
(def letters [:a :b :c]) (def numbers [1 2 3]) (def fruits ["peach" "tomato" "orange"]) (map vector letters numbers) ; => ([:a 1] [:b 2] [:c 3]) (map vector letters fruits numbers) ; => ([:a "peach" 1] [:b "tomato" 2] [:c "orange" 3])
Cool trick, but how is this useful, you ask?
Say we’ve got 3 sales reps:
(def sales-reps ["Alice" "Bob" "Carl"])
and 8 states/territories in Australia to cover:
(def au-states ["ACT" "NSW" "NT" "Qld" "SA" "Tas" "Vic" "WA"])
We don’t really care who’s assigned where, as long as each state has someone assigned to it.
(map vector sales-reps au-states) ; => (["Alice" "ACT"] ["Bob" "NSW"] ["Carl" "NT"])
Almost. You see, (map )
bombs out as soon as any of the lists runs out (in our case sales-reps
).
We’ll need to enlist the help of (cycle )
.
(cycle sales-reps) ; => ("Alice" "Bob" "Carl" "Alice" "Bob" "Carl" "Alice" ...) (take 4 (cycle sales-reps)) ; => ("Alice" "Bob" "Carl" "Alice")
Let’s try again:
(def assignments (map vector (cycle sales-reps) au-states)) ; => (["Alice" "ACT"] ["Bob" "NSW"] ["Carl" "NT"] ["Alice" "QLD"] ; ["Bob" "SA"] ["Carl" "TAS"] ["Alice" "VIC"] ["Bob" "WA"])
Nice. This time, (cycle )
returns an infinitely cycling list, so (map )
stops when au-states
runs out.
So which states were assigned to Alice
?
(defn is-alice? [tuple] (= (first tuple) "Alice")) (def alice-tuples (filter is-alice? assignments)) ; => (["Alice" "ACT"] ["Alice" "Qld"] ["Alice" "Vic"]) (defn just-states [tuple] (second tuple)) (map just-states alice-tuples) ; => ("ACT" "Qld" "Vic")
See all the states grouped by their respective sales reps:
(def grouped-by-reps (group-by first assignments)) ; => {"Alice" [["Alice" "ACT"] ["Alice" "Qld"] ["Alice" "Vic"]], ; "Bob" [["Bob" "NSW"] ["Bob" "SA"] ["Bob" "WA"]], ; "Carl" [["Carl" "NT"] ["Carl" "Tas"]]}
Clean it up a little:
(map (fn [assignment] [(first assignment) (map just-states (second assignment))]) grouped-by-reps) ; => (["Alice" ("ACT" "Qld" "Vic")] ; ["Bob" ("NSW" "SA" "WA")] ; ["Carl" ("NT" "Tas")])
With this last one, we’re mapping over grouped-by-reps
, each assignment
looks like this:
["Bob" [["Bob" "NSW"] ["Bob" "SA"] ["Bob" "WA"]]]
Breaking it down:
(first assignment) ; => "Bob" (second assignment) ; => [["Bob" "NSW"] ["Bob" "SA"] ["Bob" "WA"]] (map just-states (second assignment)) ; => ("ACT" "Qld" "Vic") ; Hence [(first assignment) (map just-states (second assignment))] ; => ["Bob" ("ACT" "Qld" "Vic")]
VoilĂ !