Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Clojure - How do I access keys in a vector of structs

Tags:

clojure

I have the following vector of structs:

(defstruct #^{:doc "Basic structure for book information."}
  book :title :authors :price)

(def #^{:doc "The top ten Amazon best sellers on 16 Mar 2010."}
  best-sellers
  [(struct book
           "The Big Short"
           ["Michael Lewis"]
           15.09)
   (struct book
           "The Help"
           ["Kathryn Stockett"]
           9.50)
   (struct book
           "Change Your Prain, Change Your Body"
           ["Daniel G. Amen M.D."]
           14.29)
   (struct book
           "Food Rules"
           ["Michael Pollan"]
           5.00)
   (struct book
           "Courage and Consequence"
           ["Karl Rove"]
           16.50)
   (struct book
           "A Patriot's History of the United States"
           ["Larry Schweikart","Michael Allen"]
           12.00)
   (struct book
           "The 48 Laws of Power"
           ["Robert Greene"]
           11.00)
   (struct book
           "The Five Thousand Year Leap"
           ["W. Cleon Skousen","James Michael Pratt","Carlos L Packard","Evan Frederickson"]
           10.97)
   (struct book
           "Chelsea Chelsea Bang Bang"
           ["Chelsea Handler"]
           14.03)
   (struct book
           "The Kind Diet"
           ["Alicia Silverstone","Neal D. Barnard M.D."]
           16.00)])

I would like to sum the prices of all the books in the vector.  What I have is the following:

(defn get-price
  "Same as print-book but handling multiple authors on a single book"
  [ {:keys [title authors price]} ]
   price)

Then I:

(reduce + (map get-price best-sellers))

Is there a way of doing this without mapping the "get-price" function over the vector? Or is there an idiomatic way of approaching this problem?

like image 313
Nick Avatar asked Jun 08 '10 03:06

Nick


1 Answers

Nice to see a Clojure 101-related question! :-)

You could map :price across best-sellers; it probably wouldn't make much of a difference as far as the degree to which this code is idiomatic is concerned. In more complex scenarios, using something like get-price might be better style and help maintainability.

As for possible more profound changes to the code, this is in fact the cleanest way to write it. One alternative would be to write a custom reduction function:

(reduce (fn [{price :price} result] (+ price result))
        0
        best-sellers)

This basically merges the map and the reduce together; occasionally this is useful, but in general, breaking down the transformation of a sequence into separate, well-defined steps helps readability & maintainability and should be the default way to go. Similar comments apply to all the other alternatives which come to my mind (including loop / recur).

All in all, I'd say you've nailed it. No tweaks to be made here. :-)

like image 186
Michał Marczyk Avatar answered Sep 23 '22 13:09

Michał Marczyk