Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I "transpose" a list of maps into a map of lists in Clojure?

Hi huys : I want to map a "average" for all values in a map. say I have a list of maps :

[{"age" 2 "height" 1 "weight" 10},
{"age" 4 "height" 4 "weight" 20},
{"age" 7 "height" 11 "weight" 40}]

And my desired output is

{"age 5 "height" 5 ....}

///Below are the ramblings of my brain, i.e. the way I might imagine this working in Clojure...not to be taken too seriously

transpose the list :

  {"age" [2 4 7] "height" [1 4 11] } 

and then I could simply do something like (again, making up a function called freduce here)

  (freduce average (vals (map key-join list)))

to get

{"age" 5 "weight" 10 "height" 7}

like image 576
jayunit100 Avatar asked Oct 23 '11 05:10

jayunit100


3 Answers

Create the map of vectors:

(reduce (fn [m [k v]]
          (assoc m k (conj (get m k []) v)))
        {}
        (apply concat list-of-maps))

Create the map of averages:

(reduce (fn [m [k v]]
          (assoc m k (/ (reduce + v) (count v))))
        {}
        map-of-vectors)
like image 113
Alex Taggart Avatar answered Oct 20 '22 20:10

Alex Taggart


Take a look at merge-with

Here's my go at some actual code:

(let [maps [{"age" 2 "height" 1 "weight" 10},
            {"age" 4 "height" 4 "weight" 20},
            {"age" 7 "height" 11 "weight" 40}]]
  (->> (apply merge-with #(conj %1 %2)
             (zipmap (apply clojure.set/union (map keys maps))
                     (repeat [])) ; set the accumulator
             maps)
       (map (fn [[k v]] [k (/ (reduce + v) (count v))]))
       (into {})))
like image 5
mange Avatar answered Oct 20 '22 20:10

mange


Here's a fairly verbose solution. Hopefully someone can come up with something better:

(let [maps [{"age" 2 "height" 1 "weight" 10},
            {"age" 4 "height" 4 "weight" 20},
            {"age" 7 "height" 11 "weight" 40}]
      ks (keys (first maps))
      series-size (count maps)
      avgs (for [k ks]
             (/ (reduce +
                        (for [m maps]
                          (get m k)))
                series-size))]
  (zipmap ks avgs))
like image 4
amalloy Avatar answered Oct 20 '22 20:10

amalloy