# Mapping over multiple collections in Clojure

## The map function maps 'n' collections to functions that recieve 'n' arguments.

#### October 4, 2021

The introductory statement above just about says it all. Here's a concrete example of that principle applied to solving problem 22 of "Project Euler". (Make sure you read the question description to understand the following code. https://projecteuler.net/problem=22)

``````(def input ["MARY", "PATRICIA", "LINDA", ...
``````

We test-drive a function for calculating the 'alphabetic' score:

``````(defn alpha-score [word]
(->> (string/lower-case word)
(map #(inc (- (int %) (int \a))))
(apply +)))
``````

This function does the following:

``````(it "computes alphabetic score"
(should= 6 (alpha-score "abc"))
(should= 53 (alpha-score "COLIN")))
``````

We test-drive a function for calculating the 'name' score:

``````(defn name-score [rank word]
(* rank (alpha-score word)))
``````

Supposing that the name "COLIN" is the 938th name in the list:

``````(it "computes name score"
(should= 49714 (name-score 938 "COLIN")))
``````

Now, to solve the problem we must:

1. Sort the input names in alphabetical order
2. Interleave the indices of each name in the list with each name
3. Make pairs of each index/name combo.
4. Pass each index/name pair to the `name-score` function
5. Sum the results of each and ever call to `name-score`

Here's a literal implementation of the above steps:

``````(defn solve-1 []
(let [indices (range 1 (inc (count input)))]
(as-> #_"step 1" (sort input) \$
#_"step 2" (interleave indices \$)
#_"step 3" (partition 2 \$)
#_"step 4" (map #(name-score (first %1) (second %1)) \$)
#_"step 5" (apply + \$))))
``````

It seems that steps 2 and 3 are really just a mapping of two collections over a function that receives two arguments...WHICH IS WHAT THE MAP FUNCTION ALREADY DOES!

Most of the time when we use `map` we are mapping a single collection over a function that receives that argument. But `map` is much more flexible in that it expects 'n' collections and a function that receives 'n' arguments.

Here's a more succinct implementation which makes use of this knowledge:

``````(defn solve-2 []
(let [indices (range 1 (inc (count input)))]
(as-> #_"step 1   " (sort input) \$
#_"steps 2-4" (map name-score indices \$)
#_"step 5   " (apply + \$))))
``````

(Of course, we could use `->>` ('thread-last') instead of `as->` ('thread-as'), but I like how 'thread-as' overtly displays both collections being passed to the `map` function in this case.)