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, <[email protected] <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 [email protected]
>> <javascript:>
>> Note that posts from new members are moderated - please be patient with
>> your first post.
>> To unsubscribe from this group, send email to
>> [email protected] <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 [email protected] <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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.