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 {"a...@mail.com" 1}}
        {:id 2 :email {"d...@mail.com" 1}}
        {:id 3 :email {"g...@mail.com" 2}}
        {:id 4 :email {"f...@mail.com" 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 {"a...@mail.com" 1}, :id 1} {:email {"d...@mail.com" 1}, :id
2} {:email {"g...@mail.com" 5}, :id 3} {:email {"f...@mail.com" 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 {"a...@mail.com" 1}}
      {:id 2 :email {"d...@mail.com" 1}}
      {:id 3 :email {"g...@mail.com" 2}}
      {:id 4 :email {"f...@mail.com" 2}}}))

This way, you can simplify the manipulation by using clojure.set :

=> (require '[clojure.set :as s])

   (def example
     (ref
       #{{:id 1 :email {"a...@mail.com" 1}}
         {:id 2 :email {"d...@mail.com" 1}}
         {:id 3 :email {"g...@mail.com" 2}}
         {:id 4 :email {"f...@mail.com" 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 {"a...@mail.com" 1}, :id 1} {:email {"f...@mail.com" 2}, :id
4} {:email {"g...@mail.com" 5}, :id 3} {:email {"d...@mail.com" 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 {"a...@mail.com" 1}}
        2 {:id 2 :email {"d...@mail.com" 1}}
        3 {:id 3 :email {"g...@mail.com" 2}}
        4 {:id 4 :email {"f...@mail.com" 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 {"a...@mail.com" 1}, :id 1}, 2 {:email {"d...@mail.com" 1},
:id 2}, 3 {:email {"g...@mail.com" 5}, :id 3}, 4 {:email
{"f...@mail.com" 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 {"a...@mail.com" 1}}
     2 {:id 2 :email {"d...@mail.com" 1}}
     3 {:id 3 :email {"g...@mail.com" 2}}
     4 {:id 4 :email {"f...@mail.com" 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 "a...@mail.com" :email-occurences 1}
        2 {:id 2 :email "d...@mail.com" :email-occurences 1}
        3 {:id 3 :email "g...@mail.com" :email-occurences 2}
        4 {:id 4 :email "f...@mail.com" :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 "a...@mail.com", :id 1}, 2
{:email-occurences 1, :email "d...@mail.com", :id 2}, 3
{:email-occurences 5, :email "g...@mail.com", :id 3}, 4
{:email-occurences 2, :email "f...@mail.com", :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 "a...@mail.com" :email-occurences 1}
        2 {:id 2 :email "d...@mail.com" :email-occurences 1}
        3 {:id 3 :email "g...@mail.com" :email-occurences 2}
        4 {:id 4 :email "f...@mail.com" :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 "a...@mail.com", :id 1}, 2
{:email-occurences 1, :email "d...@mail.com", :id 2}, 3
{:email-occurences 5, :email "g...@mail.com", :id 3}, 4
{:email-occurences 2, :email "f...@mail.com", :id 4}}



HTH,

--
Laurent

2013/6/12 Kelker Ryan <theinter...@yandex.com>:
> Here's the refactored version.
>
> user> (def example
>              (ref
>               [{:id 1 :email {"a...@mail.com" 1}}
>                {:id 2 :email {"d...@mail.com" 1}}
>                {:id 3 :email {"g...@mail.com" 2}}
>                {:id 4 :email {"f...@mail.com" 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 {"a...@mail.com" 1}, :id 1}
>  {:email {"d...@mail.com" 1}, :id 2}
>  {:email {"g...@mail.com" 5}, :id 3}
>  {:email {"f...@mail.com" 2}, :id 4}]
> nil
>
>
>
>
> 12.06.2013, 14:47, "Meikel Brandmeyer (kotarak)" <m...@kotka.de>:
>
> 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 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/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 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/groups/opt_out.


Reply via email to