· clojure

Clojure: Converting an array/set into a hash map

When I was implementing the Elo Rating algorithm a few weeks ago one thing I needed to do was come up with a base ranking for each team.

I started out with a set of teams that looked like this:

(def teams #{ "Man Utd" "Man City" "Arsenal" "Chelsea"})

and I wanted to transform that into a map from the team to their ranking e.g.

Man Utd -> {:points 1200}
Man City -> {:points 1200}
Arsenal -> {:points 1200}
Chelsea -> {:points 1200}

I had read the documentation of http://clojuredocs.org/clojure_core/1.2.0/clojure.core/array-map, a function which can be used to transform a collection of pairs into a map, and it seemed like it might do the trick.

I started out by building an array of pairs using http://clojuredocs.org/clojure_core/1.2.0/clojure.core/mapcat:

> (mapcat (fn [x] [x {:points 1200}]) teams)
("Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200})

array-map constructs a map from pairs of values e.g.

> (array-map "Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200})
("Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200})

Since we have a collection of pairs rather than individual pairs we need to use the http://clojuredocs.org/clojure_core/1.2.0/clojure.core/apply function as well:

> (apply array-map ["Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200}])
{"Chelsea" {:points 1200}, "Man City" {:points 1200}, "Arsenal" {:points 1200}, "Man Utd" {:points 1200}}

And if we put it all together we end up with the following:

> (apply array-map (mapcat (fn [x] [x {:points 1200}]) teams))
{"Man Utd"  {:points 1200}, "Man City" {:points 1200}, "Arsenal"  {:points 1200}, "Chelsea"  {:points 1200}}

It works but the function we pass to mapcat feels a bit clunky. Since we just need to create a collection of team/ranking pairs we can use the http://clojuredocs.org/clojure_core/clojure.core/vector and http://clojuredocs.org/clojure_core/clojure.core/repeat functions to build that up instead:

> (mapcat vector teams (repeat {:points 1200}))
("Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200})

And if we put the apply array-map code back in we still get the desired result:

> (apply array-map (mapcat vector teams (repeat {:points 1200})))
{"Chelsea" {:points 1200}, "Man City" {:points 1200}, "Arsenal" {:points 1200}, "Man Utd" {:points 1200}}

Alternatively we could use http://clojuredocs.org/clojure_core/clojure.core/assoc like this:

> (apply assoc {} (mapcat vector teams (repeat {:points 1200})))
{"Man Utd" {:points 1200}, "Arsenal" {:points 1200}, "Man City" {:points 1200}, "Chelsea" {:points 1200}}

I also came across the http://clojuredocs.org/clojure_core/clojure.core/into function which seemed useful but took in a collection of vectors:

> (into {} [["Chelsea" {:points 1200}] ["Man City" {:points 1200}] ["Arsenal" {:points 1200}] ["Man Utd" {:points 1200}] ])

We therefore need to change the code to use map instead of mapcat:

> (into {} (map vector teams (repeat {:points 1200})))
{"Chelsea" {:points 1200}, "Man City" {:points 1200}, "Arsenal" {:points 1200}, "Man Utd" {:points 1200}}

However, my favourite version so far uses the http://clojuredocs.org/clojure_core/clojure.core/zipmap function like so:

> (zipmap teams (repeat {:points 1200}))
{"Man Utd" {:points 1200}, "Arsenal" {:points 1200}, "Man City" {:points 1200}, "Chelsea" {:points 1200}}

I’m sure there are other ways to do this as well so if you know any let me know in the comments.

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket