开发者

idiomatic way to update maps that match a predicate in a vector

开发者 https://www.devze.com 2023-02-01 19:04 出处:网络
I have a reference to a vector [] that has maps added to it. If I want to change the value of a map item based on a predicate matching, what is the idiomatic way to do that?

I have a reference to a vector [] that has maps added to it. If I want to change the value of a map item based on a predicate matching, what is the idiomatic way to do that?

For example...

[ { :id 1 :name "Joe" } { :id 2 :name "Fred" } ]

And we want to update 开发者_Go百科any id of 2 to the name 'Brian'.


Here's one way:

(def people [ { :id 1 :name "Joe" } { :id 2 :name "Fred" } ])

(defn brian-converter [person] 
  (if (= 2 (:id person)) 
      (assoc person :name "Brian") 
      person))

(map brian-converter people)
;;=> ({:id 1, :name "Joe"} {:id 2, :name "Brian"})

Depending on how you expect those values to change, you might prefer something more flexible:

(defn create-converter [[key-to-match val-to-match] 
                        key-to-replace val-to-replace]
  (fn [person]
    (if (= val-to-match (key-to-match person))
        (assoc person key-to-replace val-to-replace)
        person)))

(map (create-converter [:id 2] :name "Brian") people)
;;=> ({:id 1, :name "Joe"} {:id 2, :name "Brian"})
(map (create-converter [:id 1] :name "Dude") people)
;;=> ({:id 1, :name "Dude"} {:id 2, :name "Fred"})

The choice of argument representation (vector for the search params, unrolled arguments for the replacements) in create-converter was kind of arbitrary for me; not sure if there's a rule for that.


Another way is to find the index of the map you want to update, then update only that map [EDIT: this is assuming you only want to update a single item in the vector]:

(def people [{:id 1 :name "Joe"} {:id 2 :name "Fred"}])

(defn vecmap-assoc-pred
  [vm pred & kvs]
  (let [i (some (fn [[i m]] (when (pred m) i))
                (map-indexed vector vm))]
    (apply update-in vm [i] assoc kvs)))

(vecmap-assoc-pred people #(= 2 (:id %)) :name "Brian")

For this particular case, it probably makes more sense to use a map of people, keyed by id:

(def people {1 {:id 1 :name "Joe"} 2 {:id 2 :name "Fred"}})
(assoc-in people [2 :name] "Brian")
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号