Pure Danger Tech


Meet my little friend mapmap

24 Sep 2010

I seem to chronically run into cases where I want to create a map in Clojure but don’t have just the right tool at hand. One nice function that I’ve found to be helpful is my little friend mapmap. mapmap is probably not a very good name – I’m open to suggestions. Maybe mapmerge would be better. It’s kind of a weird mix of zipmap, merge, and mapcat.

Here’s the code:

(defn mapmap
  "Apply kf and vf to a sequence, s, and produce a map of (kf %) to (vf %)."
  ([vf s]
     (mapmap identity vf s))
  ([kf vf s]
     (zipmap (map kf s)
              (map vf s))))

This function takes a sequence, and in the three-arg form applies a key function to the sequence, a value function to the values, and zips them together as a map. In the two-arg form, identity is used as the key function so the keys are the original sequence.

Two-arg example:

user> (mapmap #(* 10 %) [1 2 3 4 5])
{5 50, 4 40, 3 30, 2 20, 1 10}
user> (mapmap born-in [ "Chuck Berry" "Ted Nugent" ])
{"Ted Nugent" "Detroit", "Chuck Berry" "St. Louis"}

Three-arg example:

user> (mapmap #(+ 1 %) #(* 10 %) [1 2 3 4])
{5 40, 4 30, 3 20, 2 10}

And I’ve found it useful to use mapmap over a map as a sequence of map entries, for example to take a map and manipulate either the keys and/or the values of the map.

user> (mapmap #(subs (key %) 0 2) 
                      (comp name val) 
                      { "abcd" :x, "foobar" :y, "nachos" :z })
{"na" "z", "fo" "y", "ab" "x"}

Or if for example you wanted to swap the keys and values in a map:

user> (mapmap val key { :a 1 :b 2 :c 3 })
{3 :c 2 :b 1 :a}

What do you think?