I'm following this example: http://groups.google.com/group/clojure/browse_thread/thread/99b3d792b1d34b56
(see the last reply)
And this is the cryptic error that I get:
Clojure 1.2.1
user=> (def m {:a "x" :b "y" :c "z" :d "w"})
#'user/m
user=> (filter #(some % [:a :b]) m)
java.lang.IllegalArgumentException: Key must be integer
(user=>
Also I don't understand why this would even work. Isn't (some ...) going to return the first matching value, "x", every time? I'm a total noob at clojure and just trying to learn.
Please enlighten me.
I guess I just needed to read the docs more:
(select-keys m [:a :b])
Although I'm still not sure what the intention was with the example I found...
If you "iterate" over a map, you'll get key-value pairs rather than keys. For instance,
user=> (map #(str %) {:a 1, :b 2, :c 3})
("[:a 1]" "[:b 2]" "[:c 3]")
Thus your anonymous function tries to evaluate (some [:a "x"] [:a :b]) which clearly does not work.
The ideomatic solution is to use select-keys as mentioned in another answer.
(filter
(fn [x]
(some #{(key x)} [:a :b])) m)
Would do the same using filter and some (but uglier and slower).
This works by filter all from m if some [:a :b] is in the set #{(key x)} (i.e. using a set as predicate) then return the map entry.
Related
I am using clojure.walk/postwalk to compare a predicate to every map in a nested collection and want to exit with true on the first true. How would I do that? I am ok with it walking the whole data structure and then returning true if there is a true match.
As a corollary question, I guess the same question could apply to when one performs a map as opposed to a postwalk.
UPDATE: this was truly a tired/lazy question; I should have provided a code example. That said, I'm leaving it up in case anyone is currently formulating an answer to my half-baked question. The only thing that is worse than asking one is taking it down after someone has been kind enough to start helping. I will be quite content if no one answers, if they request a better question, or if they just give me suggestions of what to research.
a bit different way to do it, also employing tree-seq:
(defn find-deep [pred data not-found]
(->> data
(tree-seq coll? seq)
(some #(when (pred %) [%]))
((fnil first [not-found]))))
user> (find-deep #(= (:c %) 30) [{:a 10 :b [{:c 20 :d {:c 30}}]}] ::none)
;;=> {:c 30}
user> (find-deep #(= (:c %) 40) [{:a 10 :b [{:c 20 :d {:c 30}}]}] ::none)
;;=> :user/none
You may be interested in this function I call walk-seq. It returns a lazy depth-first sequence over a data structure which you can then seek against to find the first match. I find it to be preferable here because it doesn't require callbacks and exceptions to exit early like clojure.walk/postwalk would.
(defn walk-seq
"Returns a lazy depth-first sequence of all forms within a data structure."
[form]
(tree-seq coll? seq form))
(defn seek
"Find the first element in the collection that matches pred,
else returns not-found. Note that using seek can lead to
poor performance and you should always use indexed data
structures instead of multiple seeks over the same data."
([pred coll]
(seek pred coll nil))
([pred coll not-found]
(reduce (fn [nf x] (if (pred x) (reduced x) nf)) not-found coll)))
Usage of walk-seq:
(walk-seq {:a [{:b -1} {:b 1}] :b 2})
=>
({:a [{:b -1} {:b 1}], :b 2}
[:a [{:b -1} {:b 1}]]
:a
[{:b -1} {:b 1}]
{:b -1}
[:b -1]
:b
-1
{:b 1}
[:b 1]
:b
1
[:b 2]
:b
2)
Combining the two:
(seek (every-pred number? pos?) (walk-seq {:a [{:b -1} {:b 1}] :b 2}))
=>
1
It can be done using postwalk by throwing an exception once the predicate is true as I suggested in the comment. This approach is unconventional but concise and lets us reuse the logic of postwalk for walking the datastructure:
(defn walk-some [pred data]
(try
(clojure.walk/postwalk
#(if (pred %)
(throw (ex-info "Found" {:data %}))
%)
data)
false
(catch clojure.lang.ExceptionInfo e
true)))
(walk-some #(and (number? %) (odd? %)) {:a [[9] 3]})
;; => true
(walk-some #(and (number? %) (even? %)) {:a [[9] 3]})
;; => false
Using exceptions for control flow is rarely needed but occasionally it useful to deviate a bit from convention. You may want to define a custom exception type for improved robustness in case your predicate can throw objects of type ExceptionInfo.
I have this function:
(defn dissoc-all [m kv]
(let [[k & ks] kv]
(dissoc m k ks)))
Where m is the map and kv is the vector of keys. I use it like this:
(dissoc-all {:a 1 :b 2} [:a :b])
=>{:b 2}
This is not what I've expected. ks has :b but I don't know why it is not being use by dissoc. Anyone can help me with this?
Edit: Added question is that why is this not triggering the 3rd overload of dissoc, which is dissoc [map key & ks]?
Changed name from dissoc-in to dissoc-all as noisesmith have said, -in is not a proper name for this and I agree.
This won't work because ks is a collection of all the elements in kv after the first. So instead of :b it is [:b].
Instead, you can just use apply:
(defn dissoc-in [m vs]
(apply dissoc m vs))
Also, dissoc-in is an odd name for this function, because the standard functions with -in in the name all do nested access, and this does not use the keys to do any nested access of the map.
Why not something like this?
(defn dissoc-all [m ks]
(apply dissoc m ks))
(dissoc-all {:a 1 :b 2} [:a :b])
=> {}
The reason the third overlod of dissoc is not getting called is because it does not expect a collection of keys like [:a :b] - it expects just the keys.
For example:
(dissoc {:a "a" :b "b" :c "c" :d "d"} :a :b :c)
=> {:d "d"}
Further to noisesmith's answer:
You're being confused by the overloads/arities of dissoc, which have this simple effect:
[m & ks]
"Returns a new map of the same (hashed/sorted) type,
that does not contain a mapping for any of ks. "
The explicit arities for no keys and one key are for performance. Many clojure functions are so organised, and the documentation follows the organisation, not the underlying idea.
Now, the action of
(dissoc-all {:a 1 :b 2} [:a :b])
;{:b 2}
is to bind
k to :a
ks to [:b]
Note the latter. The example removes the :a but fails to remove the [:b], which isn't there.
You can use apply to crack open ks:
(defn dissoc-all [m kk]
(let [[k & ks] kk]
(apply dissoc m k ks)))
(dissoc-all {:a 1 :b 2} [:a :b])
;{}
... or, better, do as #noisesmith does and short-circuit the destructuring, using apply at once.
I've read this kind of thing a couple of times since I've started Clojure.
For instance, here: How to convert map to a sequence?
And in some tweet I don't remember exactly that was more or less saying "if you're using flatten you're probably doing it wrong".
I would like to know, what is wrong with flatten?
I think this is what they were talking about in the answer you linked:
so> ((comp flatten seq) {:a [1 2] :b [3 4]})
(:b 3 4 :a 1 2)
so> (apply concat {:a [1 2] :b [3 4]})
(:b [3 4] :a [1 2])
Flatten will remove the structure from the keys and values, which is probably not what you want. There are use cases where you do want to remove the structure of nested sequences, and flatten was written for such cases. But for destructuring a map, you usually do want to keep the internal sequences as is.
Anything flatten can't flatten, it ought to return intact. At the top level, it doesn't.
(flatten 8)
()
(flatten {1 2, 3 4})
()
If you think you've supplied a sequence, but you haven't, you'll get the effect of supplying an empty sequence. This is the sort of leg-breaker that most core functions take care to preclude. For example, (str nil) => "".
flatten ought to work like this:
(defn flatten [x]
(if (sequential? x)
((fn flat [y] (if (sequential? y) (mapcat flat y) [y])) x)
x))
(flatten 8)
;8
(flatten [{1 2, 3 4}])
;({1 2, 3 4})
(flatten [1 [2 [[3]] 4]])
;(1 2 3 4)
You can find Steve Miner's faster lazy version of this here.
Probability of "probably"
Listen to people who say "you're probably doing it wrong", but also do not forget they say "probably", because it all depends on the problem.
For example if your task is to flatten the map where you could care less what was the key what was the value, you just need an unstructured sequence of all, then by all means, use flatten (or apply concat).
The reason it causes a "suspicion" is the fact that you had / were given a map to begin with, hence whoever gave it to you meant a "key value" paired structure, and if you flatten it, you lose that intention, as well as flexibility and clarity.
Keep in mind
In case you are still not sure what to do with a map for you particular problem, have a for comprehension in mind, since you would have a full control on what to do with the map as you iterate of it:
create a vector?
;; can also be (apply vector {:a 34 :b 42}), but just to use "for" for all consistently
user=> (into [] (for [[k v] {:a 34 :b 42}] [k v]))
[[:a 34] [:b 42]]
create another map?
user=> (into {} (for [[k v] {:a 34 :b 42}] [k (inc v)]))
{:a 35, :b 43}
create a set?
user=> (into #{} (for [[k v] {:a 34 :b 42}] [k v]))
#{[:a 34] [:b 42]}
reverse keys and values?
user=> (into {} (for [[k v] {:a 34 :b 42}] [v k]))
{34 :a, 42 :b}
I have the following functions and reduced sample:
(defn parse-time
[time-str]
(->> time-str
(re-find #"(\d{1,2}):(\d{2}):(\d{2})")
...))
(defn coerce-times
[m & ks]
(update-in m ks parse-time))
(coerce-times {:depart "05:05:00" :arrive "05:05:00"} :depart :arrive)
This works as expected with only one key, but when I try to use multiple keys (as in the example above), I get a NPE. Line 20 is the re-find line.:
java.lang.NullPointerException: null
at java.util.regex.Matcher.getTextLength (Matcher.java:1234)
java.util.regex.Matcher.reset (Matcher.java:308)
java.util.regex.Matcher.<init> (Matcher.java:228)
java.util.regex.Pattern.matcher (Pattern.java:1088)
clojure.core$re_matcher.invoke (core.clj:4460)
clojure.core$re_find.invoke (core.clj:4512)
tempest.core$parse_time.invoke (core.clj:20)
...
Can someone please help me understand what I'm doing wrong and how I can fix this?
The keys vector provided to update-in is not a collection of keys to operate on, but a series of lookups to follow:
user> (update-in {:a {:b {:c 0}}} [:a :b :c] inc)
{:a {:b {:c 1}}}
I have an atom that has two parts to it.
(def thing (atom {:queue '() :map {}}))
I want to update both :queue and :map in one atomic stroke, to prevent them from getting off-sync.
Queue individually:
(swap! thing update-in [:queue] (list 1))
(From this question: How to append to a nested list in a Clojure atom?)
Map individually:
(swap! thing assoc-in [:map 1] (:key :value))
(From this question: Using swap to MERGE (append to) a nested map in a Clojure atom?)
How can I do these both within a single swap statement? (assuming that would prevent them from getting off-sync?)
You have one change you want to make, right? And you could write that change as a pure function? All you need to do is write that function, and pass it as the argument to swap!.
(defn take-from-queue [{q :queue, m :map}]
{:queue (rest q), :map (assoc m :new-task (first q))})
(swap! thing take-from-queue)
Where of course I have no idea what you actually want the body of your function to do, so I've made up something that doesn't throw an exception.
Say you have a hash-map atom:
(def m1 (atom {:a "A" :b "B"}))
To change :a and :b at the same time, changing their values to values that are different, say the numbers 1 and 2, use this function:
(defn my-swap! [params]
(swap! m1 (fn [old new] new) params))
, like so:
(my-swap! {:a 1 :b 2}) ;=> {:a 1, :b 2}
And the same effect could be achieved with the following function and execution:
(defn my-multi-swap! [params1 params2]
(swap! m1 (fn [old new1 new2] new2) params1 params2))
(my-multi-swap! {} {:a 1 :b 2}) ;=> {:a 1, :b 2}
Normally reset! is used if you want to ignore the old value. Here we use it:
(defn my-merge-swap! [params]
(swap! m1 (fn [old new] (merge old new)) params))
(my-merge-swap! {:b 3}) ;=> {:a "A", :b 3}
The first parameter to the swap! function is the existing value of the atom, and you must pass in one or more extra parameters, which you can use to give the atom its new value.