merging a sequence of maps - clojure

I have two maps, of the form:
(def h1 {:p1 {:x {:port :p1 :instr :x :qty 10}}})
(def h2 {:p1 {:y {:port :p1 :instr :y :qty 11}}})
When I merge them using
(apply (partial merge-with merge) [h1 h2])
I get the correct result:
-> {:p1 {:y {:port :p1, :qty 11, :instr :y}, :x {:port :p1, :qty 10, :instr :x}}
However, If I try reducing over a sequence of maps:
(reduce (fn [r m] apply (partial merge-with merge) r m) {} [h1 h2])
I get only the first map as a result:
-> {:p1 {:y {:port :p1, :qty 11, :instr :y}}}
I would expect the same result. What am I doing wrong?

You forgot to apply the apply. In
(fn [r m] apply (partial merge-with merge) r m)
... the implicit do in the fn form performs a series of evaluations, returning the result of the last one. Ignoring side-effects (and there are none), the function is equivalent to
(fn [r m] m)
as you observed.
The reduce takes the sequence of maps apart, so you don't need the apply:
(reduce (fn [r m] (merge-with merge r m)) {} [h1 h2])
; {:p1
; {:y {:qty 11, :instr :y, :port :p1},
; :x {:qty 10, :instr :x, :port :p1}}}
If you are determined to use the same structure for the function, you have to do it this way:
(reduce (fn [r m] (apply (partial merge-with merge) [r m])) {} [h1 h2])
; {:p1
; {:y {:qty 11, :instr :y, :port :p1},
; :x {:qty 10, :instr :x, :port :p1}}}
apply expects a sequence as its last argument, which it flattens into the trailing arguments to its first (function) argument.

Related

How to get an element in a matrix in clojure

I have a vector of vectors [[plate,'p1',0,1],[plate,'p2',0,2],[plate,'p3',1,1]] containing x,y positions of detected plates.
How do I retrieve the x position of plate p3?
It seems to be a simple task but I'm more familiar with python, so I'm not sure how to do this in clojure.
i would go with something like this:
(def data [[:plate "p1" 0 1] [:plate "p2" 0 2] [:plate "p3" 1 1]])
(some (fn [[_ v x y]] (when (= v "p3") [x y])) data)
;;=> [1 1]
(some (fn [[_ v x y]] (when (= v "p123") [x y])) data)
;;=> nil
(def p '[[plate,"p1",0,1],[plate,"p2",0,2],[plate,"p3",1,1]])
;; be aware, 'p1' you can use in Python, but because in Clojure `'` at beginning
;; of a symbol is parsed as `quote`, you can't use `''` instead of `""` in contrast to Python.
;; create a nested map out of the vec of vecs
(defn vecs-to-map [vecs]
(reduce (fn [m [_ id x y]] (assoc m id {:x x :y y}))
{}
vecs))
(def m (vecs-to-map p))
;;=> {"p1" {:x 0, :y 1}, "p2" {:x 0, :y 2}, "p3" {:x 1, :y 1}}
;; you can access such a nested list via `get-in` and giving the nested map
;; and the keys it should apply on it.
(get-in m ["p3" :x])
;;=> 1
Since the irregularity that one key is a string and the other a keyword is
not so nice, I would make out of them all keywords:
(defn vecs-to-map [vecs]
(reduce (fn [m [_ id x y]] (assoc m (keyword id) {:x x :y y}))
{}
vecs))
(def m (vecs-to-map p))
;; access it by:
(get-in m [:p3 :x])
;;=> 1
Additional Thoughts
We ignored the first element of the vec plate.
Let's say there exist also another vectors like
(def b '[[box "b1" 0 1] [box "b2" 0 2] [box "b3" 1 1]])
And if we want a nested map which contains :plate and :box in the
outer level as keys, we have to change the vecs-to-map function.
(defn vecs-to-map [vecs]
(reduce (fn [m [k id x y]] (assoc m (keyword k)
(assoc (get m (keyword k) {})
(keyword id) {:x x :y y})))
{}
vecs))
Then we can generate the map containing everything by:
(def m (vecs-to-map (concat p b)))
;; or alternatively in two steps:
;; (def m (vecs-to-map p))
;; (def m (merge m (vecs-to-map b)))
m
;; => {:plate {:p1 {:x 0, :y 1}, :p2 {:x 0, :y 2}, :p3 {:x 1, :y 1}}, :box {:b1 {:x 0, :y 1}, :b2 {:x 0, :y 2}, :b3 {:x 1, :y 1}}}
And we access the content by:
;; access through:
(get-in m [:plate :p3 :x])
;; => 1
(get-in m [:box :b2 :y])
;; => 2
You don't really provide much context on what you're trying to do but it feels like you want to filter the vector of tuples to those that have the symbol p3' in the second position and then return just the third and fourth elements of such a match?
If so, the following would work:
dev=> (def plate :plate)
#'dev/plate
dev=> (def data [[plate,'p1',0,1],[plate,'p2',0,2],[plate,'p3',1,1]])
#'dev/data
dev=> (let [[[_ _ x y]] (filter (comp #{'p3'} second) data)]
#_=> [x y])
[1 1]
This doesn't feel very idiomatic, so perhaps you could explain more of the context?
Note: 'p3' is a symbol whose name is p3' so I wonder if you mean "p3" for a string?
The vector of vector format doesn't seem very conducive to the sort of access you want to perform - perhaps changing it to a hash map, whose keys are the plate IDs (if that's what p1, p2, and p3 are?) would be better to work with?
Edit: in response to #leetwinkski's note about the result when there is no match, here's an alternative:
You could use when-first:
dev=> (when-first [[_ _ x y] (filter (comp #{'p123'} second) data)]
#_=> [x y])
nil
dev=> (when-first [[_ _ x y] (filter (comp #{'p3'} second) data)]
#_=> [x y])
[1 1]
Here is how I would do it, based on my favorite template project. Please also note that in Clojure strings always use double-quotes like "p1". Single quotes are totally different!
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn vec-has-label
"Returns true iff a vector has the indicated label"
[vec lbl]
(= lbl (nth vec 1)))
(defn vec->x
"Given a vector, return the x value"
[vec]
(nth vec 2))
(defn find-label
[matrix label]
(let [tgt-vecs (filterv #(vec-has-label % label) matrix) ; find all matching vectors
x-vals (mapv vec->x tgt-vecs)] ; extract the x values
x-vals))
The unit tests show the code in action
(dotest
(isnt (vec-has-label '[plate "p1" 0 1] "p0"))
(is (vec-has-label '[plate "p1" 0 1] "p1"))
(is= 9 (vec->x '[plate "p1" 9 1]))
(let [matrix (quote
[[plate "p1" 0 1]
[plate "p2" 0 2]
[plate "p3" 1 1]])]
(is= (find-label matrix "p3")
[1])
))
The unit test show the 2 ways of "quoting" a data structure that contains one or more symbols. This would be unneeded if the redundant plate symbol weren't present.
See also this list of documentation sources.

Clojure return value from a loop

I want to calculate intersection points. This works well, but I want to store the points in a vector that the function should return.
Here is my code:
(defn intersections
[polygon line]
(let [[p1 p2] line
polypoints (conj polygon (first polygon))]
(doseq [x (range (- (count polypoints) 1))]
(println (intersect p1 p2 (nth polypoints x) (nth polypoints (+ x 1))))
)))
Instead of println I want to add the result to a new vector that should be returned. How can I change it?
You need to use a for loop. The doseq function is meant for side-effects only and always returns nil. An example:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn intersect-1
[numbers]
(let [data-vec (vec numbers)]
(vec
(for [i (range (dec (count numbers)))]
{:start (nth data-vec i)
:stop (nth data-vec (inc i))}))))
The above way works, as seen by the unit test:
(dotest
(is= (intersect-1 (range 5))
[{:start 0, :stop 1}
{:start 1, :stop 2}
{:start 2, :stop 3}
{:start 3, :stop 4}])
However, it is more natural to write it like so in Clojure:
(defn intersect-2
[numbers]
(let [pairs (partition 2 1 numbers)]
(vec
(for [[start stop] pairs]
{:start start :stop stop} ))))
With the same result
(is= (intersect-2 (range 5))
[{:start 0, :stop 1}
{:start 1, :stop 2}
{:start 2, :stop 3}
{:start 3, :stop 4}]))
You can get more details on my favorite template project (including a big documentation list!). See especially the Clojure CheatSheet!
Side note: The vec is optional in both versions. This just forces the answer into a Clojure vector (instead of a "lazy seq"), which is easier to cut and paste in examples and unit tests.
Instead of for-loop, a map would be more idiomatic.
(defn intersections
[polygon line]
(let [[p1 p2] line]
(vec (map (fn [pp1 pp2] (intersect p1 p2 pp1 pp2)) polygon (cdr polygon)))))
or:
(defn intersections
[polygon line]
(let [[p1 p2] line]
(vec (map #(intersect p1 p2 %1 %2) polygon (cdr polygon)))))

Find Value of Specific Key in Nested Map

In Clojure, how can I find the value of a key that may be deep in a nested map structure? For example:
(def m {:a {:b "b"
:c "c"
:d {:e "e"
:f "f"}}})
(find-nested m :f)
=> "f"
Clojure offers tree-seq to do a depth-first traversal of any value. This will simplify the logic needed to find your nested key:
(defn find-nested
[m k]
(->> (tree-seq map? vals m)
(filter map?)
(some k)))
(find-nested {:a {:b {:c 1}, :d 2}} :c)
;; => 1
Also, finding all matches becomes a matter of replacing some with keep:
(defn find-all-nested
[m k]
(->> (tree-seq map? vals m)
(filter map?)
(keep k)))
(find-all-nested {:a {:b {:c 1}, :c 2}} :c)
;; => [2 1]
Note that maps with nil values might require some special treatment.
Update: If you look at the code above, you can see that k can actually be a function which offers a lot more possibilities:
to find a string key:
(find-nested m #(get % "k"))
to find multiple keys:
(find-nested m #(some % [:a :b]))
to find only positive values in maps of integers:
(find-nested m #(when (some-> % :k pos?) (:k %)))
If you know the nested path then use get-in.
=> (get-in m [:a :d :f])
=> "f"
See here for details: https://clojuredocs.org/clojure.core/get-in
If you don't know the path in your nested structure you could write a function that recurses through the nested map looking for the particular key in question and either returns its value when it finds the first one or returns all the values for :f in a seq.
If you know the "path", consider using get-in:
(get-in m [:a :d :f]) ; => "f"
If the "path" is unknown you can use something like next function:
(defn find-in [m k]
(if (map? m)
(let [v (m k)]
(->> m
vals
(map #(find-in % k)) ; Search in "child" maps
(cons v) ; Add result from current level
(filter (complement nil?))
first))))
(find-in m :f) ; "f"
(find-in m :d) ; {:e "e", :f "f"}
Note: given function will find only the first occurrence.
Here is a version that will find the key without knowing the path to it. If there are multiple matching keys, only one will be returned:
(defn find-key [m k]
(loop [m' m]
(when (seq m')
(if-let [v (get m' k)]
v
(recur (reduce merge
(map (fn [[_ v]]
(when (map? v) v))
m')))))))
If you require all values you can use:
(defn merge-map-vals [m]
(reduce (partial merge-with vector)
(map (fn [[_ v]]
(when (map? v) v))
m)))
(defn find-key [m k]
(flatten
(nfirst
(drop-while first
(iterate (fn [[m' acc]]
(if (seq m')
(if-let [v (get m' k)]
[(merge-map-vals m') (conj acc v)]
[(merge-map-vals m') acc])
[nil acc]))
[m []])))))

How to reduce a nested collection without using mutable state?

Given a nested collection I would like to reduce it to only the k-v pairs which are the form [_ D] where D is an integer. For instance I would like to transform as follows:
; Start with this ...
{:a {:val 1 :val 2} :b {:val 3 :c {:val 4}} :val 5}
; ... end with this
{:val 1, :val 2, :val 3, :val 4, :val 5}
I have written a function using postwalk as follows:
(defn mindwave-values [data]
(let [values (atom {})
integer-walk (fn [x]
(if (map? x)
(doseq [[k v] x]
(if (integer? v) (swap! values assoc k v)))
x))]
(postwalk integer-walk data)
#values))
I am curious if it is possible to do this without using mutable state?
EDIT The original function was not quite correct.
Your example data structure is not a legal map, so I've changed it a bit:
(defn int-vals [x]
(cond (map? x) (mapcat int-vals x)
(coll? x) (when (= 2 (count x))
(if (integer? (second x))
[x]
(int-vals (second x))))))
user> (int-vals {:a {:x 1 :y 2} :b {:val 3 :c {:val 4}} :val 5})
([:y 2] [:x 1] [:val 4] [:val 3] [:val 5])
Your requirements are a bit vague: you say "collection", but your example contains only maps, so I've just had to guess at what you intended.

How to write a dissoc-in command for clojure?

I'm looking to write a function that is similar to assoc-in but removes keys instead of adding it:
(dissoc-in {:a {:b 0}} [:a :b])
;;=> {:a {}}
I got up to here:
(def m {:a {:b {:c 1}}})
(assoc m :a (assoc (:a m) :b (dissoc (:b (:a m)) :c)))
;;=> {:a {:b {}}}
but the whole nested thing is messing with my head
I write this using update-in:
(update-in {:a {:b 0 :c 1}} [:a] dissoc :b)
=>
{:a {:c 1}}
How about:
(defn dissoc-in
"Dissociates an entry from a nested associative structure returning a new
nested structure. keys is a sequence of keys. Any empty maps that result
will not be present in the new structure."
[m [k & ks :as keys]]
(if ks
(if-let [nextmap (get m k)]
(let [newmap (dissoc-in nextmap ks)]
(if (seq newmap)
(assoc m k newmap)
(dissoc m k)))
m)
(dissoc m k)))
Example:
(dissoc-in {:a {:b 0 :c 1}} [:a :b])
Result:
{:a {:c 1}}
dissoc-in was once part of clojure.contrib.core, and is now part of core.incubator.
If you want to keep empty maps, you can alter the code slightly:
(defn dissoc-in
[m [k & ks :as keys]]
(if ks
(if-let [nextmap (get m k)]
(let [newmap (dissoc-in nextmap ks)]
(assoc m k newmap))
m)
(dissoc m k)))
Example:
(dissoc-in {:a {:b {:c 0}}} [:a :b])
Result:
{:a {}}
Here is a general solution based on update-in:
(defn dissoc-in [m p]
(if (get-in m p)
(update-in m
(take (dec (count p)) p)
dissoc (last p))
m))
Being inspired by Dominic's code. I wrote a more succinct version
(defn dissoc-in
[m [k & ks]]
(if-not ks
(dissoc m k)
(assoc m k (dissoc-in (m k) ks))))
(dissoc-in {:a {:b {:c 1}}} [:a :b :c]) ;; => {:a {:b {}}}
Another version dissoc-in2 recursively removes empty maps
(defn dissoc-in2
[m [k & ks]]
(if-not ks
(dissoc m k)
(let [nm (dissoc-in2 (m k) ks)]
(cond (empty? nm) (dissoc m k)
:else (assoc m k nm)))))
(ut/dissoc-in {:a {:b {:c 3}}} [:a :b :c])
;;; => {:a {:b {}}}
(ut/dissoc-in2 {:a {:b {:c 3}}} [:a :b :c])
;;=> {}
No need to write one, clojure.core.incubator already has a dissoc-in:
=> (dissoc-in {:children [{:name "Billy" :age 5}]} [:children 0 :age])
{:children [{:name "Billy"}]}
I'd recommend using dissoc-in from the medley library.
Here is the code as of version 0.7.0:
(defn dissoc-in
"Dissociate a value in a nested assocative structure, identified by a sequence
of keys. Any collections left empty by the operation will be dissociated from
their containing structures."
[m ks]
(if-let [[k & ks] (seq ks)]
(if (seq ks)
(let [v (dissoc-in (get m k) ks)]
(if (empty? v)
(dissoc m k)
(assoc m k v)))
(dissoc m k))
m))
Here's a link to the source code of dissoc-in in medley master.
(defn dissoc-in [m ks]
(let [parent-path (butlast ks)
leaf-key (last ks)]
(if (= (count ks) 1)
(dissoc m leaf-key)
(update-in m parent-path dissoc leaf-key))))