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.

Reply via email to