Is there a jQuery type function to solve the problem of walking through nested maps?
for example, if i have a configuration that looks like this:
(def fig
{:config
{:example
{:a "a"
:b "b"
:c "c"}
:more
{:a "a"
:b "b"
:c "c"}}})
I still haven't figured out a great way to manipulate nested persistent data structures with assoc and dissoc. However, if there was a jquery
style way to manipulate maps, then I can write code like this:
(-> fig
($ [:config :example :a] #(str % "a"))
($ [:config :b] #(str % "b")))
Giving this output:
{:config
{:example
{:a "aa"
:b "bb"
:c "c"}
:more
{:a "a"
:b "bb"
:c "c"}}}
And something like this for selectors:
($ fig [:config :example :a])
;=> "a"
($ fig [:config :b])
;=> {[:config :example :b] "b",
; [:config :more :b] "b"}
So in essence, I'm looking for an implementation of jayq
for manipulation of clojure objects instead of html doms.
Thanks in advance!
update-in
is a great function for updating nested maps.
user> (def data {:config
{:example {:a "a" :b "b" :c "c"}}
:more {:a "a" :b "b" :c "c"}})
user> (pprint (update-in data [:config :example] assoc :d 4))
{:config {:example {:a "a", :c "c", :b "b", :d 4}},
:more {:a "a", :c "c", :b "b"}}
assoc-in
may be a little closer to what you want
user> (pprint (assoc-in data [:config :example :d] 4))
{:config {:example {:a "a", :c "c", :b "b", :d 4}},
:more {:a "a", :c "c", :b "b"}}
for reading values without changing them you can use the fact that keywords look themselves up in maps to write an even more compact form than the jquery form
user> (-> data :config :example :a)
"a"
First of all, you should check out Enlive.
Otherwise: if you want to do what jQuery does (of course very simplified) - as opposed to just calling update-in:
Select:
(defn clj-query-select [obj path]
(if (empty? path)
(list obj)
(when (map? obj)
(apply concat
(remove nil?
(for [[key value] obj]
(clj-query-select
value
(if (= key (first path)) (rest path) path))))))))
For call:
(clj-query-select {:a {:b 1} :b 2} [:b])
it should yield:
(1 2)
Update/replace:
(defn clj-query-update [obj path fn]
(if (empty? path)
(fn obj)
(if (map? obj)
(into obj
(remove nil?
(for [[key value] obj]
(let [res (clj-query-update
value
(if (= key (first path)) (rest path) path)
fn)]
(when (not= res value) [key res])))))
obj)))
For call:
(clj-query-update {:c {:a {:b 1} :b 2}} [:c :b] #(* % 2))
it should yield:
{:c {:a {:b 2} :b 4}}
I didn't test it thoroughly though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With