Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vector of maps processing in Clojure

Tags:

clojure

I haven't found much documentation or coding examples for doing manipulations on vectors of maps. For instance, if I have

(def student-grades 
 [{:name "Billy" :test1 74 :test2 93 :test3 89} 
  {:name "Miguel" :test1 57 :test2 79 :test3 85}
  {:name "Sandy" :test1 86 :test2 97 :test3 99}
  {:name "Dhruv" :test1 84 :test2 89 :test3 94}])

and I want to add or associate a new key value pair for the test averages, which functions should I read up on? Also if anyone knows of any reference/resources for vectors of maps in Clojure, please share! Thanks so much!

like image 933
Adam Avatar asked Aug 13 '11 16:08

Adam


2 Answers

In this case, you want to map a function over the collection (which just happens to be a vector); for each element in the collection (which happens to be a map -- unfortunate naming collision there), you want to generate a new map that has all the key-value pairs of the old map, plus a new key, let's say :avg.

e.g.

(into [] ; optional -- places the answer into another vector
  (map   ; apply the given function to every element in the collection
    (fn [sg] ; the function takes a student-grade
      (assoc sg ; and with this student-grade, creates a new mapping
        :avg    ; with an added key called :avg
        (/ (+ (:test1 sg) (:test2 sg) (:test3 sg)) 3.0)))
    student-grades  ; and the function is applied to your student-grades vector
))

ps you can use (doc fn-name) to get documentation on it; if you're new to Clojure, I'd advise hanging out with the friendly folks on irc.freenode.net #clojure and read a book - my favorite is currently Programming Clojure, but I'm awaiting O'Reilly's upcoming Clojure book with bated breath.

like image 79
michel-slm Avatar answered Sep 22 '22 16:09

michel-slm


Hircus has already provided a good answer, but here's another implementation for comparison:

(defn average [nums]
  (double (/ (apply + nums) (count nums))))

(map 
  #(assoc % :avg (average ((juxt :test1 :test2 :test3) %)))
  student-grades)

=> ({:avg 85.33333333333333, :name "Billy", :test1 74, :test2 93, :test3 89} etc....)

Comments to note:

  • It's usually worth separating out generic functionality such as "average" into a separate, well-named function
  • juxt is quite a useful function in general to extract a specific list of component values from a map
like image 22
mikera Avatar answered Sep 19 '22 16:09

mikera