假设我有一个嵌套的地图结构,例如
{:val1 {:m1 1 :m2 2 :m3 2} :val2 {:m1 4 :m2 8 :m3 7}}
此示例只有两个值,但一般情况下可能会有更多值。我知道每个嵌套映射的键都相同(上例中的m1,:m2和:m3)。我有一个关键字列表,比如说
[:m1 :m3]
我希望将每个内部地图的值除以列表中给出的每个关键字的数字,例如5。继续我的例子,我想得到
{:val1 {:m1 1/5 :m2 2 :m3 2/5} :val2 {:m1 4/5 :m2 8 :m3 7/5}}
我该怎么做?对于固定的内部键,例如:m1,我可以做
(map #(update-in % [1 :m1] / 5) nested-map)
但我不确定如何将其推广到关键字列表。谢谢!
答案 0 :(得分:1)
在Clojure Cookbook的脚步中,我定义了
(defn map-vals [f m]
(zipmap (keys m) (map f (vals m))))
...然后使用核心功能来做你想做的事:
(defn map-inner-keys-with [ks f m]
(map-vals
(fn [vm] (into vm (map (juxt identity (comp f vm)) ks)))
m))
例如,
(map-inner-keys-with [:m1 :m3] #(/ % 5)
{:val1 {:m1 1 :m2 2 :m3 2}
:val2 {:m1 4 :m2 8 :m3 7}})
=> {:val1 {:m1 1/5, :m2 2, :m3 2/5}, :val2 {:m1 4/5, :m2 8, :m3 7/5}}
答案 1 :(得分:0)
这是一个可行的答案,假设您的嵌套级别是恒定的(根据您的示例,这似乎是一个公平的假设)。请注意,我提供了一个键作为一个集合,如果你想使用一个向量,你可以简单地修改函数,将一个符号绑定到函数顶部的let中的哈希集调用。
(defn change-map [m f ks]
(for [[k1 v1] m]
(hash-map k1 (into {} (for [[k2 v2] v1]
(if (contains? ks k2) [k2 (f v2)]
[k2 v2]))))))
(change-map example #{:m1 :m3})
或者,如果您希望一次只传入一个键:
(defn change-map [m f & ks]
(let [ks (apply hash-set ks)]
(for [[k1 v1] m]
(hash-map k1 (into {} (for [[k2 v2] v1]
(if (contains? ks k2) [k2 (f v2)]
[k2 v2])))))))
(change-map example #(/ % 2) :m1 :m3)
经过一番思考后,上面的例子并不适合我。并不是说他们错了,只觉得过于设计。我认为下面的内容更接近您的想法,并且更为简洁。显然,您可以通过将硬编码/ 2
更改为函数来概括这一点。
(into {} (map (fn[[k v]] {k (reduce #(update %1 %2 / 2) v [:m1 :m3])}) example))
答案 2 :(得分:0)
您首先可以定义一个在详细级别工作的函数:
(def data {:val1 {:m1 1 :m2 2 :m3 2} :val2 {:m1 4 :m2 8 :m3 7}})
正如您所看到的,某些地图条目值将被转换,而其他地图条目值将保持不变。
您的示例输入数据:
f
因此,我们在此处查看外部结构以应用详细信息(defn make-changes [m f]
(->> m
(map (fn [[k v]]
[k (->> v
(map f)
(into {}))]))
(into {})))
:
f
当然apply-effect
将是(make-changes data apply-effect)
;;=> {:val1 {:m1 1/5, :m2 2, :m3 2/5}, :val2 {:m1 4/5, :m2 8, :m3 7/5}}
:
make-changes
仔细查看上面的(defn map-map-entries [f m]
(->> m
(map f)
(into {})))
函数两次使用此抽象:
map-map-entries
所以我们现在可以使用(map-map-entries
(fn [[k v]]
[k (map-map-entries
apply-effect
v)])
data)
来回答这个问题:
line = "1 34 1/10/40G IPS,2/4/8/10/16G FC Module DS-X9334-K9 ok"
print " ".join(line.split()[2:-2])
答案 3 :(得分:0)
您可以使用specter进行此转换:
(:require [clojure.test :refer :all]
[com.rpl.specter :as specter])
(deftest ^:focused transform-test
(let [selectors #{:m1 :m3}
input {:val1 {:m1 1 :m2 2 :m3 2} :val2 {:m1 4 :m2 8 :m3 7}}
output {:val1 {:m1 1/5 :m2 2 :m3 2/5} :val2 {:m1 4/5 :m2 8 :m3 7/5}}]
(is (= output
(specter/transform [specter/MAP-VALS
specter/ALL
#(contains? selectors (first %))
specter/LAST]
#(/ % 5)
input)))))