我需要更改嵌套地图中的值,我不知道密钥的值。我想出了以下内容。
;; input {String {String [String]}}
;; output {String {String String}}
(defn join-z
[x-to-y-to-z]
(zipmap (keys x-to-y-to-z)
(map (fn [y-to-z] (into {} (map (fn [[y z]] {y (clojure.string/join z)})
(seq y-to-z))))
(seq (vals x-to-y-to-z)))))
(def example
{"a" {"b" ["c" "d" "e"]}
"m" {"n" ["o" "p"]}})
;; (join-z example) => {"m" {"n" "op"}, "a" {"b" "cde"}}
这似乎是一个黑客攻击。什么是惯用的clojure来做到这一点?或者,有什么像Haskell的镜头库吗?
更新:基于user5187212
回答
(defn update-vals [f m0]
(reduce-kv (fn [m k v] (assoc m k (f v)))
{}
m0))
;; (update-vals clojure.string/join {"b" ["c" "d" "e"]}) => {"b" "cde"}
(defn join-z [x-to-y-to-z]
(update-vals (partial update-vals clojure.string/join) x-to-y-to-z))
;; (join-z example) => {"m" {"n" "op"}, "a" {"b" "cde"}}
这看起来更优雅。谢谢!
答案 0 :(得分:2)
简短的回答是肯定的,就是你这样做的方式:)
我会选择更像这样的东西:
(into {} (for [[k v] example]
[k (into {} (for [[k2 v2] v]
[k2 (string/join v2)]))]))
这几乎是一回事。
有一个名为Spectre的库 https://github.com/nathanmarz/specter 查询和转换:
(ns specter.core
(:require
[clojure.string :as string]
[com.rpl.specter :as s]))
(def example
{"a" {"b" ["c" "d" "e"]}
"m" {"n" ["o" "p"]}})
(s/transform
[s/ALL s/LAST s/ALL s/LAST]
string/join
example)
我认为这是表达它的一种非常巧妙的方式。
答案 1 :(得分:2)
我建议reduce-kv
。
对于最后一层,您可以使用以下内容:
(defn foo [x]
(reduce-kv
(fn [m k v]
(assoc m k (clojure.string/join v)))
{}
x))
然后根据需要多次调用它......
(reduce-kv
(fn [m k v]
(assoc m k (foo v)))
{}
example)
另一种方法可能超过all nested keys然后
(reduce
(fn [m ks]
(update-in m ks clojure.string/join))
example
all-nested-keys)