18

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!

2 Answers 2

12

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks so much! I've just been poking around clojuredocs.org and I couldn't seem to find relevant examples for collections of collections.
No problem! I guess it does not come up as an issue, once you're used to it -- the beauty of functional programming is that the building blocks just stack up, so when dealing with the outer collection, you can simply treat the inner collections, abstractly, as mere elements, and likewise when writing the function to transform one of these elements, you don't have to worry about the outer collection. You'll get the hang of it :)
You can also use (mapv ...) as a convenience for (into [] (map ...)).
Have you read O'Reilly's Clojure Programming? How do you think it compares to Programming Clojure?
@Josh need to read it while I still have my Safari Bookshelf trial. Will let you know!
6

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

3 Comments

Thanks, I'm learning so many useful functions, going to add juxt to that list!
No worries! p.s. the usage of juxt in this case is a bit clever, since it is exploiting the fact that you can use keywords (":test1" etc.) as functions. But you can use it with any arbitrary functions.
The inventor's paradox: Sometimes solving a general problem can be easier than solving a specific problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.