Testing vectors and nested vectors in Clojure - clojure

Is there a way in Clojure to test a vector and see if it's nested, i.e. a way to test [:a :b :c :d] vs. [[:a :b] [:c :d]]?
I've tried the test
(vector? [:a :b :c :d])
true
but it remains true for nested vectors as well,
(vector? [[:a :b] [:c :d]])
true

checking if any of them are sequential seems close:
user> (every? #(not (sequential? %)) [:a :b :c :d])
true
user> (every? #(not (sequential? %)) [:a :b :c :d [:e]])
false
because all the base collections can be made into sequences, though it may be necessary to also check for Java arrays:
(every? #(not (sequential? %)) [:a :b :c :d (into-array [1 2 3])])

vector? returns true if its argument is a vector (implements IPersistentVector). [:a :b :c :d] is a vector. So is [[:a :b] [:c :d]]. Therefore, calling vector? on either of them will return true.
Now, we can say a vector is nested if any of its elements is a vector. We can test for this using some and the vector? predicate:
(defn nested-vector? [v]
(some vector? v))
This will test specifically for vectors. However, you might want to take a more general approach that applies to any Sequential data structure:
(defn nested? [coll]
(some sequential? coll))

Related

How do I exit a Clojure walk postwalk on a nested maps on the first true predicate match?

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.

clojure behavior of (into {} '((:a :b) (:c :d)))

user=> (into {} '((:a :b) (:c :d)))
Throws: ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry clojure.lang.ATransientMap.conj (ATransientMap.java:44).
Whereas:
user=> (into {} (list [:a :b] [:c :d]))
Is fine. It's a strange difference, since many times other functions return lists when the thing they had to begin with was a vector:
user=> (into {} (partition 2 (interleave [:a :b] [:c :d])))
Will throw, because it partition 2 ...) results in ((:a :c) (:b :d)). So it's pretty annoying. You basically have to memorize both the return types of methods and the specific behaviors of functions like into, or you have to just let stuff blow up and fix it as you find it with stuff like (into {} (map vec (partition 2 (interleave [:a :b] [:c :d])))).
Is there a specific reason why into doesn't like the pairs as lists?
The reason is as you state, only a vector pairs can be used to build maps. I don't know of a practical reason why this limitation exists. But there are also several other methods for constructing hash-maps. If you find yourself using partition, perhaps the answer is to use an alternate construction method.
If you have parallel sequences of keys and values:
(zipmap [:a :c] [:b :d])
If you have all the items in a flat sequence:
(apply hash-map [:a :b :c :d])
Building a map from a sequence:
(into {} (for [[k v] xs]
[k (transform v)]))
I never realized this wouldn't work! Don't forget:
(apply hash-map (interleave [:a :b] [:c :d]))
;=> {:b :d, :a :c}
since hash-map implicitly creates pairs from the scalar args:
(hash-map :a :c :b :d)
;=> {:b :d, :a :c}
you don't really need the (partition 2...) which is the source of the problem.

dissoc in clojure can't get to work

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.

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 to check if a nested key exists in a map

I want to check if every key given in a vector [:e [:a :b] [:c :d]] exists in a map.
{:e 2 :a {:b 3} :c {:d 5}}
I could write the following to check -
(def kvs {:e 2 :a {:b 3} :c {:d 5}})
(every? #(contains? kvs %) [[:e] [:a :b] [:c :d]])
However the above would fail as contains doesnt check the key one level deep like update-in does. How do I accomplish the above ?
An improvement on murtaza's basic approach, which also works when the map has nil or false values:
(defn contains-every? [m keyseqs]
(let [not-found (Object.)]
(not-any? #{not-found}
(for [ks keyseqs]
(get-in m ks not-found)))))
user> (contains-every? {:e 2 :a {:b 3} :c {:d 5}}
[[:e] [:a :b] [:c :d]])
true
user> (contains-every? {:e 2 :a {:b 3} :c {:d 5}}
[[:e] [:a :b] [:c :d :e]])
false
The following does it -
(every? #(get-in kvs %) [[:e] [:a :b] [:c :d]])
Any other answers also welcome !
How about this:
(every? #(if (vector? %)
(contains? (get-in kvs (drop-last %)) (last %))
(contains? kvs %)) [:e [:a :b] [:c :d]])