Your two functions can be written more succinctly (and with fewer explicit conditionals) like these, but they will be harder for a beginner to understand. (The nested update-in with fnil in particular may cause confusion.)
(defn add-placeholder-to-history [users] (reduce-kv (fn [users uk {:keys [history]}] (assoc-in users [uk :history] (into [{}] (take 2 history)))) users users)) (defn update-history [users {:keys [category prize-money winner]}] (update-in users [winner] (fnil update-in {:history [{}]}) [:history 0 category] (fnil + 0) prize-money)) You may have a good pedagogical reason for your current implementation, but I would take a different approach: build the new history entries first, then add them to the users. (defn winnings-by-user [contests] (reduce (fn [user+winning {:keys [category winner prize-money]}] (update-in user+winning [winner category] (fnil + 0) prize-money)) {} contests)) (defn update-histories [users new-winnings] (let [all-user-keys (set (concat (keys users) (keys new-winnings)))] (reduce (fn [users ukey] (update-in users [ukey :history] #(into [(get new-winnings ukey {})] (take 2 %)))) users all-user-keys))) (update-histories users (winnings-by-user contests)) On Tuesday, May 26, 2015 at 12:37:34 AM UTC-5, Chris Freeman wrote: > > It seems like, if you don't mind doing the assoc all the time, you could > replace the whole if with something like: > > (assoc nu (:winner c) (or ((:winner c) nu) {:history [{}]})) > > You might want to wrap that in a let so you don't repeat (:winner c). > > Also, it looks like add-placeholder-to-history could be a map over an > update-in instead of a loop, like: > > (defn prepend-hash [x] > (into [{}] x)) > > (defn add-placeholder-to-history [us] > (into {} (map #(update-in % [1 :history] prepend-hash) us))) > > Chris > > > > On Mon, May 25, 2015 at 5:07 PM, <lawr...@waleup.com <javascript:>> wrote: > >> >> I started to write an essay, aimed at those programmers who are >> experienced with highly mutable languages such as Javascript, Ruby, PHP, >> etc, to demonstrate how one's code style changes when one switches to a >> mostly immutable language such as Clojure. However, my Clojure code is not >> nearly as terse as I wanted. In particular, I have this nil check: >> >> (if (nil? ((:winner c) nu)) >> (assoc nu (:winner c) {:history [{}]}) >> nu) >> >> which I assume I am writing because I am ignorant. I'm guessing there >> might be something clever I can do to avoid this? >> >> For my code examples, I'm working with these 2 data structures: >> >> (def users { >> :henry { >> :history >> [ >> {:housing 25, :restaurants 40, :theater 930}, >> {:restaurants 30, :crisis 220} >> ] >> }, >> :lisa { >> :history >> [ >> {:theater 80}, >> {:housing 445, :restaurants 15, :theater 35} >> ] >> }, >> :pasha { >> :history >> [ >> {:restaurants 5}, >> {:restaurants 40, :theater 60} >> ] >> }, >> :eli { >> :history >> [ >> {:crisis 135, :restaurants 440, :theater 65}, >> {:theater 95} >> ] >> } >> }) >> >> (def contests [{:category :housing, :prize-money 100, :winner :eli}, >> {:category :housing, :prize-money 30, :winner :henry}, >> {:category :housing, :prize-money 340, :winner :henry}, >> {:category :housing, :prize-money 45, :winner :susan}, >> {:category :housing, :prize-money 15, :winner :henry}, >> {:category :housing, :prize-money 10, :winner :pasha}, >> {:category :housing, :prize-money 25, :winner :pasha}, >> {:category :crisis, :prize-money 100, :winner :eli}, >> {:category :crisis, :prize-money 2330, :winner :henry}, >> {:category :crisis, :prize-money 90, :winner :henry}, >> {:category :restaurants, :prize-money 1130, :winner :eli}, >> {:category :restaurants, :prize-money 130, :winner :pasha}, >> {:category :theater, :prize-money 60, :winner :eli}, >> {:category :theater, :prize-money 90, :winner :pasha}, >> {:category :theater, :prize-money 130, :winner :pasha}, >> {:category :theater, :prize-money 830, :winner :susan}, >> {:category :theater, :prize-money 90, :winner :susan}, >> {:category :theater, :prize-money 270, :winner :eli}]) >> >> Presumably "users" shows past winnings from 2 rounds of some contest, >> whereas "contests" shows the winnings from the 3rd round, which need to be >> added to "users". So I wrote: >> >> >> (defn add-placeholder-to-history [us] >> (loop [u us nu {}] >> (if (first u) >> (recur >> (rest u) >> (assoc nu (get (first u) 0) {:history (into [] (cons {} >> (:history (get (first u) 1))))})) >> nu))) >> >> (defn update-history [nu c] >> (update-in >> (if (nil? ((:winner c) nu)) >> (assoc nu (:winner c) {:history [{}]}) >> nu) >> [(:winner c) :history 0 (:category c)] (fnil #(+ %1 (:prize-money >> c)) 0))) >> >> And so in the end we would simply call: >> >> (reduce >> update-history >> (add-placeholder-to-history users) >> contests) >> >> Which correctly gives me: >> >> { >> :susan { >> :history [{:theater 920, :housing 45}] >> } >> :lisa { >> :history [{} >> {:theater 80} >> {:housing 445, :restaurants 15, :theater 35}] >> } >> :henry { >> :history [{:crisis 2420, :housing 385} >> {:housing 25, :restaurants 40, :theater 930} >> {:crisis 220, :restaurants 30}] >> } >> :eli { >> :history [{:theater 330, :restaurants 1130, :crisis 100, :housing 100} >> {:crisis 135, :restaurants 440, :theater 65} >> {:theater 95}] >> } >> :pasha { >> :history [{:theater 220, :restaurants 130, :housing 35} >> {:restaurants 5} >> {:restaurants 40, :theater 60}] >> } >> } >> >> And then I wrote: >> >> ----------------------------------- >> >> By the way, you might be wondering what this is for: >> >> (if (nil? ((:winner c) nu)) >> (assoc nu (:winner c) {:history [{}]}) >> nu) >> >> We do this for Susan. She is a new contestant who won some money in the >> newest round of contests, however, she does not yet exist in "users", so we >> need to create a space for her. Without these 3 lines of code, we get this >> for her: >> >> {:susan {:history {0 {:theater 920, :housing 45}}}, >> >> But with these 3 lines of code, we get the correct results for her, and >> for everyone else. >> >> ----------------------------------- >> >> I am wondering if I can avoid those 3 lines of code? >> >> >> >> >> -- >> You received this message because you are subscribed to the Google >> Groups "Clojure" group. >> To post to this group, send email to clo...@googlegroups.com >> <javascript:> >> Note that posts from new members are moderated - please be patient with >> your first post. >> To unsubscribe from this group, send email to >> clojure+u...@googlegroups.com <javascript:> >> For more options, visit this group at >> http://groups.google.com/group/clojure?hl=en >> --- >> You received this message because you are subscribed to the Google Groups >> "Clojure" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to clojure+u...@googlegroups.com <javascript:>. >> For more options, visit https://groups.google.com/d/optout. >> > > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.