简体   繁体   English

如何在Clojure中用y更新地图值x?

[英]How to update map values x with y in Clojure?

I am trying to make a function in Clojure that would take a map, x and y values as parameters and then go through all the elements in a map and potentially replace all x values (if any) with y. 我试图在Clojure中创建一个函数,该函数将地图,x和y值作为参数,然后遍历地图中的所有元素,并可能用y替换所有x值(如果有)。

(defn Change-x-to-y-in-map [map x y]; code here)

For example, if I would have a map like {:a 1 :b 2 :c 1} and I would call the function with parameters [map 1 "it works!"], the function should return the following map: {:a "it works!" :b 2 :c "it works!} 例如,如果我有一个像{:a 1 :b 2 :c 1}这样的映射,并且我要使用参数[map 1“ it works!”]调用该函数,则该函数应返回以下映射: {:a "it works!" :b 2 :c "it works!} {:a "it works!" :b 2 :c "it works!} . {:a "it works!" :b 2 :c "it works!}

So it would replace all keys with value 1 to keys with value "it works!". 因此,它将所有值为1的键替换为值为“ it!”的键。

Thank you already beforehand for your help! 预先感谢您的帮助!

You can do this generically over any form with clojure.walk functions: 您可以使用clojure.walk函数以任何形式通用地执行此操作:

(defn replace-vals [m v r]
  (walk/postwalk
    (fn [e] (if (= e v) r e))
    m))

(replace-vals {:a 1 :b 2 :c 1} 1 "Hey!")
=> {:a "Hey!", :b 2, :c "Hey!"}

(replace-vals [1 2 3 4 1] 1 "Hey!")
=> ["Hey!" 2 3 4 "Hey!"]

This will also work for nested forms. 这也适用于嵌套表单。

(replace-vals {:a 1 :b {:c 1 :d "Bye!"}} 1 "Hey!")
=> {:a "Hey!", :b {:c "Hey!", :d "Bye!"}}

If you want to only replace map values you could refactor to this: 如果只想替换映射 ,则可以重构为:

(defn replace-map-vals [m v r]
  (walk/prewalk
    (fn [e] (if (and (map-entry? e) (= (val e) v))
              [(key e) r]
              e))
    m))

(replace-map-vals {1 "not replaced" :replaced 1} 1 "Hey!")
=> {1 "not replaced", :replaced "Hey!"})

Note this version uses prewalk due to an issue with postwalk and map entries . 注意,由于postwalk和map条目存在问题,此版本使用prewalk

A reducible-friendly version would use reduce-kv directly: 可简化的版本将直接使用reduce-kv

(defn update-v [m ov nv] 
  (reduce-kv (fn [acc k v]
              (assoc acc k (if (= v ov) nv v)))
             {} m))

(update-v {:x 1 :y 2 :z 1} 1 "It Works!") => {:x "It Works!", :y 2, :z "It Works!"} (update-v {:x 1:y 2:z 1} 1“有效!”)=> {:x“有效!”,:y 2,:z“有效!”}

(I hereby put this code under Apache 2.0 license if you can't take it under the SO default CC-BY SA) (如果您不能在默认的CC-BY SA下使用此代码,则将其置于Apache 2.0许可下)

Starting from Stefan's answer , we can modify the initial map instead of building a new one: Stefan的答案开始 ,我们可以修改初始地图,而不用构建一个新地图:

(defn update-v [m ov nv] 
  (reduce-kv (fn [acc k v] (if (= v ov) (assoc acc k nv) acc))
             m m))

And we can use a transient to make the modifications to: 我们可以使用瞬态对以下内容进行修改:

(defn update-v [m ov nv] 
  (persistent!
    (reduce-kv (fn [acc k v] (if (= v ov) (assoc! acc k nv) acc))
             (transient m) m)))

These changes should (hmmmmmm) speed things up a little. 这些更改应(hmmmmmm)加快速度。

(I hereby put this code under Apache 2.0 license if you can't take it under the SO default CC-BY SA) (如果您不能在默认的CC-BY SA下使用此代码,则将其置于Apache 2.0许可下)

One way is to iterate through the keys and values of the map with for and use into to build up the new map: 一种方式是通过与地图的键和值迭代for ,并使用into建立新的地图:

(defn val-replace [m x y]
  (into {} (for [[k v] m]
             [k (if (= v x) y v)])))

> (val-replace {:x 1 :y 2 :z 1} 1 "It Works!")
{:x "It Works!", :y 2, :z "It Works!"}

> (val-replace1 {:a "" :b 4 :c ""} "" nil)
{:a nil, :b 4, :c nil}

Specter solution to boot: 幽灵引导解决方案:

(defn replace-vals [m v r] 
    (setval [MAP-VALS (partial = v)] r m))

The function map-vals already exists in the Tupelo library . Tupelo库中已经存在map-vals 函数 It applies a function tx-fn to every value in the map, creating a new output map. 它将函数tx-fn应用于映射中的每个值,从而创建新的输出映射。 The unit tests show it in action: 单元测试显示了它的作用:

  (let [map-123 {:a 1 :b 2 :c 3}
        tx-fn   {1 101 2 202 3 303}]
    (is= (t/map-vals map-123 inc)   {:a   2, :b   3, :c   4})
    (is= (t/map-vals map-123 tx-fn) {:a 101, :b 202, :c 303}))

In the first case, the function inc is applied to each value in the input map (IM). 在第一种情况下,函数inc应用于输入映射(IM)中的每个值。

The 2nd example uses a "transformation map" as tx-fn , where each [kv] pair indicates the desired transformation from old to new values, respectivly, in IM. 第二个示例使用“转换图”作为tx-fn ,其中每个[kv]对分别表示IM中从旧值到新值的所需转换。 Thus we see values in map-123 changed as: 因此,我们看到map-123值更改为:

1 -> 101
2 -> 202 
3 -> 303

A sister function map-keys is also available: 也可以使用姊妹功能map-keys

(dotest
  (let [map-123 {1 :a 2 :b 3 :c}
        tx-fn   {1 101 2 202 3 303}]
    (is= (t/map-keys map-123 inc)   {  2 :a   3 :b   4 :c})
    (is= (t/map-keys map-123 tx-fn) {101 :a 202 :b 303 :c}))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM