Recursion 将具有默认值的键添加到嵌套clojure hashmap
我试着做Clojure,但还是坚持使用嵌套的hashmap。 我有一个这样的结构:Recursion 将具有默认值的键添加到嵌套clojure hashmap,recursion,clojure,hashmap,clojurescript,Recursion,Clojure,Hashmap,Clojurescript,我试着做Clojure,但还是坚持使用嵌套的hashmap。 我有一个这样的结构: {:type "view" children: [ {:type "view" :id "123"} {:type "view" :children [ {:type "view"}]}]} {:type "view" :id "43434" children: [ {:type "view" :id "123
{:type "view"
children: [
{:type "view"
:id "123"}
{:type "view"
:children [
{:type "view"}]}]}
{:type "view"
:id "43434"
children: [
{:type "view"
:id "123"}
{:type "view"
:id "456"
:children [
{:type "view"
:id "5656"}]}]}
现在我想用随机字符串(如果不存在)将字段:id
添加到每个hashmap。要得到这样的东西:
{:type "view"
children: [
{:type "view"
:id "123"}
{:type "view"
:children [
{:type "view"}]}]}
{:type "view"
:id "43434"
children: [
{:type "view"
:id "123"}
{:type "view"
:id "456"
:children [
{:type "view"
:id "5656"}]}]}
您可以使用
clojure.walk/postwark
执行此操作:
(walk/postwalk
(fn [v]
(if (and (map? v) (nil? (:id v)))
(assoc v :id (str (rand-int 9999)))
v))
data)
=>
{:type "view"
:id "3086"
:children [{:type "view"
:id "123"}
{:type "view"
:id "8243"
:children [{:type "view" :id "3222"}]}]}
…其中,
数据
是您的输入映射postwalk
正在遍历嵌套映射,并且assoc
在每个没有嵌套映射的映射上使用:id
键(随机整数字符串)。您可以使用clojure.walk/postwalk
执行此操作:
(walk/postwalk
(fn [v]
(if (and (map? v) (nil? (:id v)))
(assoc v :id (str (rand-int 9999)))
v))
data)
=>
{:type "view"
:id "3086"
:children [{:type "view"
:id "123"}
{:type "view"
:id "8243"
:children [{:type "view" :id "3222"}]}]}
…其中,
数据
是您的输入映射postwalk
正在遍历嵌套的映射,并且assoc
在每个没有嵌套映射的映射上使用:id
键(一个随机整数字符串)。除了walk
变量之外,还有一些其他变量:
最简单的解决方案是递归更新
(defn upd [{:keys [id children] :as data}]
(assoc data
:id (if id
id
(str (rand-int Integer/MAX_VALUE)))
:children (mapv upd children)))
#'user/upd
user> (upd data)
;;=> {:type "view",
;; :children [{:type "view", :id "123", :children []}
;; {:type "view",
;; :children [{:type "view", :id "35223257",
;; :children []}],
;; :id "551012526"}],
;; :id "1899442274"}
您也可以使用clojure的拉链:
(require '[clojure.zip :as z])
(loop [curr (z/zipper map? :children (fn [n ch] (assoc n :children (vec ch))) data)]
(if (z/end? curr)
(z/root curr)
(recur (z/next
(let [node (z/node curr)]
(if (:id node)
curr
(z/replace curr
(assoc node :id
(str "id-" (rand-int Integer/MAX_VALUE))))))))))
;;=> {:type "view",
;; :children [{:type "view", :id "123"}
;; {:type "view",
;; :children [{:type "view", :id "id-92807721"}],
;; :id "id-1357268462"}],
;; :id "id-803083206"}
除了
walk
变体之外,还有一些其他变体:
最简单的解决方案是递归更新
(defn upd [{:keys [id children] :as data}]
(assoc data
:id (if id
id
(str (rand-int Integer/MAX_VALUE)))
:children (mapv upd children)))
#'user/upd
user> (upd data)
;;=> {:type "view",
;; :children [{:type "view", :id "123", :children []}
;; {:type "view",
;; :children [{:type "view", :id "35223257",
;; :children []}],
;; :id "551012526"}],
;; :id "1899442274"}
您也可以使用clojure的拉链:
(require '[clojure.zip :as z])
(loop [curr (z/zipper map? :children (fn [n ch] (assoc n :children (vec ch))) data)]
(if (z/end? curr)
(z/root curr)
(recur (z/next
(let [node (z/node curr)]
(if (:id node)
curr
(z/replace curr
(assoc node :id
(str "id-" (rand-int Integer/MAX_VALUE))))))))))
;;=> {:type "view",
;; :children [{:type "view", :id "123"}
;; {:type "view",
;; :children [{:type "view", :id "id-92807721"}],
;; :id "id-1357268462"}],
;; :id "id-803083206"}
很好的解决方案,但是请注意,如果输入数据有另一个映射(不仅是
:children
,而且还有例如:attributes
),它们也会递归地分配ID,因为这个变量不关心数据的语义,而是修改每个映射。尽管如此,如果数据结构不受未来某些更改的影响,也可以。使用(and(map?v)(nil?(:id v))(=(:type v)“view”)
可能解决了@leetwinski的concernsice解决方案,但请注意,如果输入数据有另一个映射(不仅是:children
,还包括例如:attributes
),它们还可以递归地分配ID,因为这种变体不关心数据的语义,而是修改每个映射。尽管如此,如果数据结构不受未来某些更改的影响,也可以。使用(and(map?v)(nil?(:id v))(=(:type v)“view”)
可能会解决@leetwinski的问题