Reputation: 14415
Jumping right in, better way of doing this:
(assoc-in
{:children [{:children [{:children [{:children [{:children []}]}]}]}]}
[:children 0 :children 0 :children 0 :children 0 :children 0]
:hello)
I want to insert :hello
into deepest :children
vector. Above I am doing it with assoc-in
.
Is there a better way than assoc-in
?
Or, if when assoc-in
is the only way, how would you handle assoc-in
's 2nd argument [k & ks]
?
Also good to know if there it something that also works for inserting :world
into and arbitrary :children
's vector... like the 3rd child or the 2nd child's 1st child.
Upvotes: 5
Views: 644
Reputation: 6956
You could also use clojure.walk
for this
(require '[clojure.walk :as w])
(def nested-map
{:children [{:children [{:children [{:children [{:children []}]}]}]}]})
(w/postwalk (fn [node] (if (and (vector? node) (empty? node))
(conj node :hello)
node))
nested-map)
=> {:children [{:children [{:children [{:children [{:children [:hello]}]}]}]}]}
Upvotes: 3
Reputation: 26446
The vector argument to assoc-in
need not be a literal, so you can construct it as desired.
(def nested-map
{:children [{:children [{:children [{:children [{:children []}]}]}]}]})
(assoc-in nested-map (vec (take 10 (cycle [:children 0]))) :hello)
;=> {:children [{:children [{:children [{:children [{:children [:hello]}]}]}]}]}
Or for 3rd child of the 2nd child of the 1st child, construct a path like
(vec (interleave (repeat :children) [0 1 2]))
;=> [:children 0 :children 1 :children 2]
More generally you can use zippers to arbitrarily move around the nested map, e.g. descend last child. The movement functions are can be composed, etc.
(require '[clojure.zip :as zip])
(def z (zip/zipper map? :children #(assoc % :children (vec %2)) nested-map))
(-> (ffirst (filter (comp zip/end? second) ; find last
(partition 2 1 (iterate zip/next z))))
(zip/edit (constantly :hello)) ; change to :hello
zip/root) ; bubble up changes
;=> {:children [{:children [{:children [{:children [{:children [:hello]}]}]}]}]}
Upvotes: 6