clojure prewalk with select-keys - clojure

(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.

Related

clojure/clojurescript change just one key in nested map

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}}}

Getting the "unread" part of a string being read with Clojure's read-string

Clojure's (read-string) is really useful.
eg.
(read-string "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")
will give me the first object, the {:a 1 :b 2}
But how can I get the rest of string ie. "{:c 3 :d 4} [1 2 3]"
What's the reader equivalent of rest or drop ?
You can wrap the string in a StringReader, then wrap that in a PushbackReader, then read from that reader multiple times.
NB. the example below uses clojure.edn/read, as that is an edn-only reader meant for dealing with pure data; clojure.core/read is primarily meant for reading code and should never be used with untrusted inputs.
(require '[clojure.edn :as edn])
(def s "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")
;; Normally one would want to use with-open to close the reader,
;; but here we don't really care and we don't want to accidentally
;; close it before consuming the result:
(let [rdr (java.io.PushbackReader. (java.io.StringReader. s))
sentinel (Object.)] ; ← or just use ::eof as sentinel
(take-while #(not= sentinel %)
(repeatedly #(edn/read {:eof sentinel} rdr))))
;= ({:a 1, :b 2} {:c 3, :d 4} [1 2 3])
ClojureScript version of what should be the accepted answer by https://stackoverflow.com/users/232707/michał-marczyk
(require '[cljs.reader :as rdr])
(require '[cljs.tools.reader.reader-types :as reader-types])
(def s "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")
(let [pbr (reader-types/string-push-back-reader s)
sentinel ::eof]
(take-while #(not= sentinel %)
(repeatedly #(rdr/read {:eof sentinel} pbr))))
Probably not very idiomatic but straightforward
(->> (str "(" "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]" ")")
(read-string))
then access to individual elements (you can also use brackets)
If you have a list within the string, you can preserve it via options given to read-string-
(def str-list "({:a 1 :b 2} {:c 3 :d 4} [1 2 3])")
(read-string {:read-cond :preserve} str-list)
;;=> ({:a 1 :b 2} {:c 3 :d 4} [1 2 3])
The source for the available options can be found in the doc string the of read function, i.e. (source read)from the REPL.

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

how to represent a seq in midje test

(fact "Checking :time has been removed"
(remove-date [{:time 1 :a 2} {:c 3 :time 4}]) => (seq '({:a 2} {:c 4})))
In the above test the remove-date function returns a seq ({:a 2} {:c 4}) How do I represent the seq on the right hand side ? (The above doesnt work)
The above works for me, you just got the {:c 4} in your assertion wrong. It should be {:c 3}.
(fact "Checking :time has been removed"
(remove-date [{:time 1 :a 2} {:c 3 :time 4}]) => (seq '({:a 2} {:c 3})))
In fact you don't need the seq call:
(fact "Checking :time has been removed"
(remove-date [{:time 1 :a 2} {:c 3 :time 4}]) => '({:a 2} {:c 3}))
I tested it with midje 1.5.0 and clojure 1.4.0

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}))