如何在 Clojure 中用 y 更新映射值 x?
How to update map values x with y in Clojure?
我正在尝试在 Clojure 中创建一个函数,它将一个地图、x 和 y 值作为参数,然后遍历地图中的所有元素,并可能将所有 x 值(如果有)替换为 y。
(defn Change-x-to-y-in-map [map x y]; code here)
例如,如果我有一个类似 {:a 1 :b 2 :c 1}
的映射,并且我将使用参数 [map 1 "it works!"] 调用该函数,该函数应该 return 以下映射: {:a "it works!" :b 2 :c "it works!}
。
所以它会将所有值为 1 的键替换为值为 "it works!" 的键。
预先感谢您的帮助!
一种方法是使用 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}
您可以在具有 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!"]
这也适用于嵌套表单。
(replace-vals {:a 1 :b {:c 1 :d "Bye!"}} 1 "Hey!")
=> {:a "Hey!", :b {:c "Hey!", :d "Bye!"}}
如果您只想替换 map values,您可以重构为:
(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!"})
请注意,由于 an issue with postwalk
and map entries,此版本使用 prewalk
。
A reducible-friendly 版本将直接使用 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!"}
(如果您不能在 SO 默认 CC-BY SA 下使用,我特此将此代码置于 Apache 2.0 许可下)
从开始,我们可以修改初始地图而不是构建新地图:
(defn update-v [m ov nv]
(reduce-kv (fn [acc k v] (if (= v ov) (assoc acc k nv) acc))
m m))
我们可以使用瞬态来修改:
(defn update-v [m ov nv]
(persistent!
(reduce-kv (fn [acc k v] (if (= v ov) (assoc! acc k nv) acc))
(transient m) m)))
这些更改应该 (hmmmmmm) 稍微加快速度。
(如果您不能在 SO 默认 CC-BY SA 下使用,我特此将此代码置于 Apache 2.0 许可下)
函数 map-vals
已经存在 in the Tupelo library。它将函数 tx-fn
应用于地图中的每个值,创建一个新的输出地图。单元测试展示了它的实际效果:
(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}))
在第一种情况下,函数 inc
应用于输入映射 (IM) 中的每个值。
第二个示例使用 "transformation map" 作为 tx-fn
,其中每个 [k v] 对分别表示 IM 中从旧值到新值的所需转换。因此我们看到 map-123
中的值更改为:
1 -> 101
2 -> 202
3 -> 303
姐妹函数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}))
Spectre开机解决方案:
(defn replace-vals [m v r]
(setval [MAP-VALS (partial = v)] r m))
我正在尝试在 Clojure 中创建一个函数,它将一个地图、x 和 y 值作为参数,然后遍历地图中的所有元素,并可能将所有 x 值(如果有)替换为 y。
(defn Change-x-to-y-in-map [map x y]; code here)
例如,如果我有一个类似 {:a 1 :b 2 :c 1}
的映射,并且我将使用参数 [map 1 "it works!"] 调用该函数,该函数应该 return 以下映射: {:a "it works!" :b 2 :c "it works!}
。
所以它会将所有值为 1 的键替换为值为 "it works!" 的键。
预先感谢您的帮助!
一种方法是使用 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}
您可以在具有 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!"]
这也适用于嵌套表单。
(replace-vals {:a 1 :b {:c 1 :d "Bye!"}} 1 "Hey!")
=> {:a "Hey!", :b {:c "Hey!", :d "Bye!"}}
如果您只想替换 map values,您可以重构为:
(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!"})
请注意,由于 an issue with postwalk
and map entries,此版本使用 prewalk
。
A reducible-friendly 版本将直接使用 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!"}
(如果您不能在 SO 默认 CC-BY SA 下使用,我特此将此代码置于 Apache 2.0 许可下)
从
(defn update-v [m ov nv]
(reduce-kv (fn [acc k v] (if (= v ov) (assoc acc k nv) acc))
m m))
我们可以使用瞬态来修改:
(defn update-v [m ov nv]
(persistent!
(reduce-kv (fn [acc k v] (if (= v ov) (assoc! acc k nv) acc))
(transient m) m)))
这些更改应该 (hmmmmmm) 稍微加快速度。
(如果您不能在 SO 默认 CC-BY SA 下使用,我特此将此代码置于 Apache 2.0 许可下)
函数 map-vals
已经存在 in the Tupelo library。它将函数 tx-fn
应用于地图中的每个值,创建一个新的输出地图。单元测试展示了它的实际效果:
(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}))
在第一种情况下,函数 inc
应用于输入映射 (IM) 中的每个值。
第二个示例使用 "transformation map" 作为 tx-fn
,其中每个 [k v] 对分别表示 IM 中从旧值到新值的所需转换。因此我们看到 map-123
中的值更改为:
1 -> 101
2 -> 202
3 -> 303
姐妹函数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}))
Spectre开机解决方案:
(defn replace-vals [m v r]
(setval [MAP-VALS (partial = v)] r m))