clojure/clojurescript change just one key in nested map - replace

I have this map:
{:a {:a {:a 1 :b 2}}}
And I want to turn it into this one:
{:a {:a {:x 1 :b 2}}}
I tried this, but -of course- got all :a replaced:
(clojure.walk/postwalk-replace {:a :c} {:a {:a {:a 1 :b 2}}})
-> {:c {:c {:c 1, :b 2}}}
I tried this, but got a result I can't even interpret:
(update-in {:a {:a {:a 1 :b 2}}} [:a :a] clojure.walk/postwalk-replace {:a :c})
-> {:a {:a {1 :c}}}
What can I do?

There is a clojure.set/rename-keys. E.g.
(update-in {:a {:a {:a 1 :b 2}}} [:a :a] clojure.set/rename-keys {:a :c})
; → {:a {:a {:b 2, :c 1}}}
The reason, why your example fails is the argument order.
postwalk-replace needs the first argument to be the replacement map
and the second argument to what is to be renamed. But update-in
always sends the traversed things as first argument into the function.
So you need to juggle the arguments around (e.g. via an anon-fn or with
partial):
(update-in {:a {:a {:a 1 :b 2}}} [:a :a] (partial clojure.walk/postwalk-replace {:a :c}))
; → {:a {:a {:b 2, :c 1}}}

Related

Clojure custom function for threading macro

I have a map and I want to write a custom function for updating it.
(-> {:a 1 :b 2}
(fn [x] (update x :a inc)))
This of course is a simple example and could be easily done without the function wrapped around the update, but it shows what I want to do. But this gives me the following error.
Syntax error macroexpanding clojure.core/fn at (core.clj:108:1).
{:a 1, :b 2} - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
{:a 1, :b 2} - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body
I don't get why this is not working, since the threading macro should but my map as first parameter in the function, right?
You can always use macroexpand to see what happened. In your case, macroexpand will return you:
(fn {:a 1, :b 2} [x] (update x :a inc))
obviously this is not a valid function. But if you tweak it this way:
(-> {:a 1 :b 2}
(#(update % :a inc)))
the expanded form will then become valid:
(#(update % :a inc) {:a 1, :b 2})
You don't put a function itself to be called, but call the function without the first parameter, For your example it would be:
> (-> {:a 1 :b 2}
(update :a inc))
{:a 2, :b 2}
This is easier to see by expanding the macro in each case
> (macroexpand-1 '(-> {:a 1 :b 2} (update :a inc)))
(update {:a 1, :b 2} :a inc)
> (macroexpand-1 '(-> {:a 1 :b 2} (fn [x] (update x :a inc))))
(fn {:a 1, :b 2} [x] (update x :a inc))
As #jas and #rmcv pointed out, I was giving the threading macro the function itself, not the call of a function without the argument. So in short terms the solution would be
(-> {:a 1 :b 2}
((fn [x] (update x :a inc))))
I don't think any of these solutions are the simplest. I would propose choosing one of the following:
A. Use the normal threading form:
(-> {:a 1, :b 2}
(update :a inc)) => {:a 2, :b 2}
Everyone is used to seeing this and can understand it easily. Since you have already rejected this approach, I assume you think the code is clearer by using a named parameter.
B. Use a named function
(defn updater [x] (update x :a inc))
(-> {:a 1, :b 2}
updater) => {:a 2, :b 2}
(-> {:a 1, :b 2}
(updater)) => {:a 2, :b 2}
This is more how the -> form was envisioned to work. I think the 2nd version is the clearest, as it is the most consistent where all function expressions have parentheses (single arg or multi-arg).
C. Consider using the it-> macro from the Tupelo Library:
(it-> {:a 1, :b 2}
(update it :a inc)) => {:a 2, :b 2}
Much like the named function, the expression is normal Clojure form without the "invisible" parameter silently inserted into the update expression. The pronoun it serves as the temporary placeholder for the threaded value (an idea copied from Groovy). Simple, explicit, and flexible, since the it can be in the first, last, or any other parameter location:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )

clojure prewalk with select-keys

(clojure.walk/prewalk #(if (map? %)
(select-keys % [:c])
%)
{:a 1 :b [{:c 3} {:d 4}] :c 5})
=>{:c 5}
why does this only find {:c 5} and not also {:c 3}?
I'm trying to write something that will pull out all key/value pairs that exist for any form and at any level for the key I specify.
When it your function is called with
{:c 5, :b [{:c 3} {:d 4}], :a 1}
...it returns:
{:c 5}
...thus discarding all other keys, including the :b branch, which is thus not traversed.

Strange thing with apply/merge-with, in Clojure

Who can explain me this fact:
user> ((partial merge-with +) {:a 1} {:a 2})
{:a 3}
user> (apply (partial merge-with +) ({:a 1} {:a 2}))
nil
Why do I get nil in the second case?
What is wrong with the second line?
The expression
({:a 1} {:a 2})
evaluates to nil. Maps in Clojure are functions which takes a key and returns the corresponding value. The expression
(let [f {:a 1}]
(f {:a 2}))
which is equivalent to ({:a 1} {:a 2}) tries to lookup the key {:a 2} in the map {:a 1} and since there is no such key in the map nil is returned.
Going back to your original problem, all you have to do is to change the list ({:a 1} {:a 2}) to a vector [{:a 1} {:a 2}] and it will work as expected. Note also that you don't need partial in this particular case, (apply merge-with + [{:a 1} {:a 3}]) will work just fine.
In the second case, when you ({:a 1} {:a 2}), as maps act as functions which get values from them, what you're doing is equivalent to (get {:a 1} {:a 2}) and, as {:a 2} is not a key in {:a 1}, you get nil. Then, aplying the function over nil gets nil.
What you have to do is either quote the list, such as not evaluate it as a function application
user=> (apply (partial merge-with +) '({:a 1} {:a 2}))
{:a 3}
or use a vector (which is more idiomatic in clojure)
user=> (apply (partial merge-with +) [{:a 1} {:a 2}])
{:a 3}
Juan Manuel

Update-in nested map

I'm new to clojure and I've been staring at this for some time, I'm sure there's something basic I just don't see. I want to conj two sets, but they're nested, example:
(def foo {:b #{:test}})
(def bar {:a {:b #{:ab}} :c :d})
I tried:
=>(update-in bar [:a :b] conj (:b foo) )
{:a {:b #{#{:test} :ab}}, :c :d}
I guess that makes sense, but what I wanted was {:a {:b #{:test :ab}}, :c :d}
I just can't seem how to get either #{:test} out of the set to conj it, or to properly access :b as a set given the update-in syntax.
Any help is enormously appreciated.
You need to use into instead of conj:
(update-in bar [:a :b] into (:b foo))
;= {:a {:b #{:test :ab}}, :c :d}

How do I transform a sequences of maps into a sequence of maps with selected keys?

I have a sequence of map like this
({:a 1 :b 2 : c 4} {:a 3 :b 3 :d 4})
And I want to turn this into a sequence of more compact maps that just have the :a and :b keys, like this:
({:a 1 :b 2} {:a 3 :b 3})
What's the most concise way to do this?
The built-in function select-keys is what you're looking for.
(let [in [{:a 1 :b 2 :c 4} {:a 3 :b 3 :d 4}]]
(map #(select-keys % [:a :b])
in))
A more generic solution would be to write a function that takes the keys you want to keep and returns a fn on maps. Then map it over the sequence of maps:
(defn keep-keys
[ks]
(fn [m] (select-keys m ks)))
(map (keep-keys [:a :b]) '({:a 1 :b 2 :c 4} {:a 3 :b 3 :d 4}))