Hello,
A note about the code organization : I think you should strive to
separate the code that does side effects from the code that works on
the datastructures.
This means you could refactor your refactored version as such:
=> (def example
(ref
[{:id 1 :email {"[email protected]" 1}}
{:id 2 :email {"[email protected]" 1}}
{:id 3 :email {"[email protected]" 2}}
{:id 4 :email {"[email protected]" 2}}]))
(defn update-counter-val [v id]
(let [at-after (drop-while #(not= id (:id %)) v)
mod-key (-> at-after first :email keys first)
location (- (count v) (count at-after))]
(update-in v [location :email mod-key] inc)))
(defn update-counter [id xs]
(dosync (alter xs update-counter-val id)))
(dotimes [_ 3]
(update-counter 3 example))
@example
#'user/example
#'user/update-counter-val
#'user/update-counter
nil
[{:email {"[email protected]" 1}, :id 1} {:email {"[email protected]" 1}, :id
2} {:email {"[email protected]" 5}, :id 3} {:email {"[email protected]" 2},
:id 4}]
Finally, you could save you some pain if you had a representation of
the data that is more amenable to manipulation.
Depending on your requirements:
- do you really need a vector, or can you go with a set ?
e.g. change example to
(def example
(ref
#{{:id 1 :email {"[email protected]" 1}}
{:id 2 :email {"[email protected]" 1}}
{:id 3 :email {"[email protected]" 2}}
{:id 4 :email {"[email protected]" 2}}}))
This way, you can simplify the manipulation by using clojure.set :
=> (require '[clojure.set :as s])
(def example
(ref
#{{:id 1 :email {"[email protected]" 1}}
{:id 2 :email {"[email protected]" 1}}
{:id 3 :email {"[email protected]" 2}}
{:id 4 :email {"[email protected]" 2}}}))
(defn update-counter-val [v id]
(let [r (first (s/select #(= id (:id %)) v))
mod-key (-> r :email keys first)]
(-> v (disj r) (conj (update-in r [:email mod-key] inc)))))
(defn update-counter [id xs]
(dosync (alter xs update-counter-val id)))
(dotimes [_ 3]
(update-counter 3 example))
@example
nil
#'user/example
#'user/update-counter-val
#'user/update-counter
nil
#{{:email {"[email protected]" 1}, :id 1} {:email {"[email protected]" 2}, :id
4} {:email {"[email protected]" 5}, :id 3} {:email {"[email protected]" 1},
:id 2}}
Or alternatively, you can have the records indexed in a hashmap:
=> (require '[clojure.set :as s])
(def example
(ref
{1 {:id 1 :email {"[email protected]" 1}}
2 {:id 2 :email {"[email protected]" 1}}
3 {:id 3 :email {"[email protected]" 2}}
4 {:id 4 :email {"[email protected]" 2}}}))
(defn update-counter-val [v id]
(let [mod-key (-> id v :email keys first)]
(update-in v [id :email mod-key] inc)))
(defn update-counter [id xs]
(dosync (alter xs update-counter-val id)))
(dotimes [_ 3]
(update-counter 3 example))
@example
nil
#'user/example
#'user/update-counter-val
#'user/update-counter
nil
{1 {:email {"[email protected]" 1}, :id 1}, 2 {:email {"[email protected]" 1},
:id 2}, 3 {:email {"[email protected]" 5}, :id 3}, 4 {:email
{"[email protected]" 2}, :id 4}}
Also, restructuring the record a little bit could help, but I don't
understand what it's about, so I don't know what to suggest.
Anyway, you could get rid of computing mod-key by leveraging some more
core clojure functions:
(require '[clojure.set :as s])
(def example
(ref
{1 {:id 1 :email {"[email protected]" 1}}
2 {:id 2 :email {"[email protected]" 1}}
3 {:id 3 :email {"[email protected]" 2}}
4 {:id 4 :email {"[email protected]" 2}}}))
(defn update-counter-val [v id]
(letfn [(inc-email [m k v] (assoc m k (inc v)))]
(update-in v [id :email] #(reduce-kv inc-email {} %))))
(defn update-counter [id xs]
(dosync (alter xs update-counter-val id)))
(dotimes [_ 3]
(update-counter 3 example))
@example
(note : this example is a little bit more general: it increments each
value of each email found in the map under the :email key)
finally, if there is only one email per record, then maybe the
structure of the record should be simplified (and the resulting code)
as such:
=> (require '[clojure.set :as s])
(def example
(ref
{1 {:id 1 :email "[email protected]" :email-occurences 1}
2 {:id 2 :email "[email protected]" :email-occurences 1}
3 {:id 3 :email "[email protected]" :email-occurences 2}
4 {:id 4 :email "[email protected]" :email-occurences 2}}))
(defn update-counter-val [v id]
(update-in v [id :email-occurences] inc))
(defn update-counter [id xs]
(dosync (alter xs update-counter-val id)))
(dotimes [_ 3]
(update-counter 3 example))
@example
nil
#'user/example
#'user/update-counter-val
#'user/update-counter
nil
{1 {:email-occurences 1, :email "[email protected]", :id 1}, 2
{:email-occurences 1, :email "[email protected]", :id 2}, 3
{:email-occurences 5, :email "[email protected]", :id 3}, 4
{:email-occurences 2, :email "[email protected]", :id 4}}
At this point, it becomes arguable, again, if update-counter-val
should exist on its own. Inlining it again inside update-counter then
gives:
=> (require '[clojure.set :as s])
(def example
(ref
{1 {:id 1 :email "[email protected]" :email-occurences 1}
2 {:id 2 :email "[email protected]" :email-occurences 1}
3 {:id 3 :email "[email protected]" :email-occurences 2}
4 {:id 4 :email "[email protected]" :email-occurences 2}}))
(defn update-counter [id xs]
(dosync (alter xs update-in [id :email-occurences] inc)))
(dotimes [_ 3]
(update-counter 3 example))
@example
nil
#'user/example
#'user/update-counter
nil
{1 {:email-occurences 1, :email "[email protected]", :id 1}, 2
{:email-occurences 1, :email "[email protected]", :id 2}, 3
{:email-occurences 5, :email "[email protected]", :id 3}, 4
{:email-occurences 2, :email "[email protected]", :id 4}}
HTH,
--
Laurent
2013/6/12 Kelker Ryan <[email protected]>:
> Here's the refactored version.
>
> user> (def example
> (ref
> [{:id 1 :email {"[email protected]" 1}}
> {:id 2 :email {"[email protected]" 1}}
> {:id 3 :email {"[email protected]" 2}}
> {:id 4 :email {"[email protected]" 2}}]))
> #'user/example
> user>
> (defn update-counter [id xs]
> (dosync
> (let [_ @xs
> at-after (drop-while #(not= id (:id %)) _)
> mod-key (-> at-after first :email keys first)
> location (- (count _) (count at-after))]
> (alter xs update-in [location :email mod-key] inc))))
> #'user/update-counter
> user> (dotimes [_ 3]
> (update-counter 3 example))
> nil
> user> (clojure.pprint/pprint @example)
> [{:email {"[email protected]" 1}, :id 1}
> {:email {"[email protected]" 1}, :id 2}
> {:email {"[email protected]" 5}, :id 3}
> {:email {"[email protected]" 2}, :id 4}]
> nil
>
>
>
>
> 12.06.2013, 14:47, "Meikel Brandmeyer (kotarak)" <[email protected]>:
>
> Hi,
>
> Am Mittwoch, 12. Juni 2013 06:39:59 UTC+2 schrieb Kelker Ryan:
>
> user> (defn update-counter [id xs]
>
> (let [at-after (drop-while #(not= id (:id %)) @xs)
> to-modify (-> at-after first :email)
> mod-key (-> to-modify keys first)
> location (let [_ (- (count @xs) (count at-after))]
> (if-not (pos? _) 0 _))]
> (dosync
> (alter xs update-in [location :email mod-key] inc))))
>
>
> You have to wrap all accesses to xs in the same dosync.
>
> Kind regards
> Meikel
>
>
> --
> --
> 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/groups/opt_out.
>
>
--
--
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/groups/opt_out.