tags:

views:

111

answers:

1

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?

+7  A: 

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. :-)

Michał Marczyk
Thank you for your quick response Michal.
Nick