Replace multiple values in a nested map in Clojure - clojure

I just spent hours trying to figure this out and even searching for the previous questions but the solutions don't seem to apply so I decided to create a new one.
I have an existing map with certain keys and values that I want to replace with values from another map.
(def m '{a {:*x 0 :*velx 1, :*vely 1}})
(def m' '{a {:*velx 9, :*vely 9}})
(assoc (m 'a) (m' 'a))
;; => {a {:*x 0 :*velx 9, :*vely 9}}
Since assoc needs to take in the content of the map, this doesn't seem to work and I don't know how to 'remove the curly brakets' from it...
Edit: this is different to the suggested existing thread as it's not a list of maps but rather two separate maps and the proposed solution doesn't work in my case.

I managed to find it finally!
(merge-with into m m')
=> {a {:*x 0, :*velx 9, :*vely 9}}

Related

clojure - trouble destructing map inside macro

I'm a newbie in clojure, so please bear with me.
Writing a macro as so:
`(let [query# (:query-params ~'+compojure-api-request+)
options# (select-keys query# [:sort-by :from :to])])
First line of the let block destructures a query-params from http request - which produces this structure:
{sort-by billing-account/name, from 0, to 10, payment-due , payment-method , search }
And the trouble is with the second line - it returns an empty map when I use select-keys, however when I say for example (first query#) - the output looks like this: [sort-by billing-account/name]
Could anyone please explain why the select-keys does not work?
P.S. Tried (get query# :from) & (:from query#) - no luck there as well.
UPD
Keys were strings, not keywords - therefore using strings as keys works just fine.
By the way, you can also destructure string keys with :strs:
(let [m {"sort-by" "billing-account/name",
"from" "0",
"to" "10",
"payment-due" nil,
"payment-method", "search"}
{:strs [sort-by from to payment-due payment-method]} m]
(println sort-by from to payment-due payment-method))
;;=> billing-account/name 0 10 nil search
See https://clojure.org/guides/destructuring for a full description of the destructuring syntax.
I think you are confused by the differences between keywords, symbols and strings. In your comment you say that they're symbols, but in your edit you say they're strings.
You should read up on the difference:
in Clojure, why have Strings, Keywords AND Symbols?
Why does Clojure have "keywords" in addition to "symbols"?
The idiomatic thing is to usually prefer using keywords as map keys, although stuff that comes from the internet (json, http headers, etc) is sometimes all strings.
To answer your question directly, the keys passed to select-keys need to be equal (using the = function) to the ones in the map, so in this case they need to be the same type.
;; For example
(select-keys {'foo 1 'bar 2} ['foo]) ;=> {foo 1}
(select-keys {:foo 1 :bar 2} [:foo]) ;=> {:foo 1}
(select-keys {"foo" 1 "bar" 2} ["foo"]) ;=> {"foo" 1}
Also I question the need for this to be a macro, is there a reason that a plain function won't work?

update or assoc a list rather than a vector

Updating a vector works fine:
(update [{:idx :a} {:idx :b}] 1 (fn [_] {:idx "Hi"}))
;; => [{:idx :a} {:idx "Hi"}]
However trying to do the same thing with a list does not work:
(update '({:idx :a} {:idx :b}) 1 (fn [_] {:idx "Hi"}))
;; => ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Associative clojure.lang.RT.assoc (RT.java:807)
Exactly the same problem exists for assoc.
I would like to do update and overwrite operations on lazy types rather than vectors. What is the underlying issue here, and is there a way I can get around it?
The underlying issue is that the update function works on associative structures, i.e. vectors and maps. Lists can't take a key as a function to look up a value.
user=> (associative? [])
true
user=> (associative? {})
true
user=> (associative? `())
false
update uses get behind the scenes to do its random access work.
I would like to do update and overwrite operations on lazy types
rather than vectors
It's not clear what want to achieve here. You're correct that vectors aren't lazy, but if you wish to do random access operations on a collection then vectors are ideal for this scenario and lists aren't.
and is there a way I can get around it?
Yes, but you still wouldn't be able to use the update function, and it doesn't look like there would be any benefit in doing so, in your case.
With a list you'd have to walk the list in order to access an index somewhere in the list - so in many cases you'd have to realise a great deal of the sequence even if it was lazy.
You can define your own function, using take and drop:
(defn lupdate [list n function]
(let [[head & tail] (drop n list)]
(concat (take n list)
(cons (function head) tail))))
user=> (lupdate '(a b c d e f g h) 4 str)
(a b c d "e" f g h)
With lazy sequences, that means that you will compute the n first values (but not the remaining ones, which after all is an important part of why we use lazy sequences). You have also to take into account space and time complexity (concat, etc.). But if you truly need to operate on lazy sequences, that's the way to go.
Looking behind your question to the problem you are trying to solve:
You can use Clojure's sequence functions to construct a simple solution:
(defn elf [n]
(loop [population (range 1 (inc n))]
(if (<= (count population) 1)
(first population)
(let [survivors (->> population
(take-nth 2)
((if (-> population count odd?) rest identity)))]
(recur survivors)))))
For example,
(map (juxt identity elf) (range 1 8))
;([1 1] [2 1] [3 3] [4 1] [5 3] [6 5] [7 7])
This has complexity O(n). You can speed up count by passing the population count as a redundant argument in the loop, or by dumping the population and survivors into vectors. The sequence functions - take-nth and rest - are quite capable of doing the weeding.
I hope I got it right!

How to parse nested parameters in a Clojure defn?

I was browsing some Clojure source code and came across this function:
(defn draw-mask [app-state [r c]]
(let [x1 (+ 25 (* c 50))
y1 (+ 25 (* r 50))]
[:circle {:cx x1 :cy y1 :r 12 :fill "white"}]))
What I do not understand is how [app-state [r c]] is parsed. What would a typical data structure passed to this and how does defn parcel it out. Any references to this in the clojure docs would be appreciated, especially since ClojureDocs.org was of no help on the subject.
Correction
As #Josh points out, sequential destructuring requires nth to work: seqability is not enough.
This is a simple case of sequential destructuring. Let's use a function that shows what's going on:
(defn foo [app-state [r c]]
{:app-state app-state, :r r, :c c})
The first argument, app-state, can be anything.
The second argument, [r c], must be something like a sequence that
nth applies to. Then
r is its first element, and
c is its second.
If the sequence isn't long enough, these yield nil.
Examples:
(foo 1 ())
;{:app-state 1, :r nil, :c nil}
(foo inc "hello, world!")
;{:app-state #<core$inc clojure.core$inc#32d23c2f>, :r \h, :c \e}
(foo :one [:two :three :four])
;{:app-state :one, :r :two, :c :three}
(foo "Flubalub" (drop 5 (iterate #(* 10 %) 1)))
;{:app-state "Flubalub", :r 100000, :c 1000000}
But
(foo 99 #{1 2})
;java.lang.UnsupportedOperationException: nth not supported on this type: PersistentHashSet
; ...
The function draw-mask in your example takes two arguments. The first is app-state, which is not used in your code, and the second can be one of several different types of data: values from a map, a string, a list, or most commonly, a vector. You can see the different types here that can be used in nthFrom in the clojure code.
This is called sequential destructuring, and as other comments above mention, it's a big topic. However, for your case, this is how it works:
(draw-mask xyz [3 9]) ---> In draw-mask, r is 3, and c is 9.
(draw-mask xyz [3]) ---> In draw-mask, r is 3, and c is nil.
(draw-mask xyz [3 9 12]) ---> In draw-mask, r is 3, and c is 9 -- 12 is not bound
One clarification here: whether or not a structure is seqable? is not the main criteria for being able to be destructured. For example, a set is seqable? but cannot be destructured. The main criteria for sequential destructuring (different from associative destructuring, using maps, which is not discussed here) is that nth must be supported on it. In RT.java, you will see the list of possible types. They are: CharSequence, a native Java array, RandomAccess, Matcher, Map.Entry, and Sequential (Sequential will cover the most common structures: list and vector).

"nth not supported" on PersistentHashSet when destructuring Set in Loop header

Clojure noob here.
I want to pull the front and rest out of a Set. Doing (front #{1}) and (rest #{1}) produce 1 and () respectively, which is mostly what I'd expect.
However in the code below, I use the destructuring [current-node & open-nodes] #{start} in my loop to pull something out of the set (at this point I don't really care about if it was the first or last item. I just want this form working) and it breaks.
Here's my function, half-implementing a grid search:
(defn navigate-to [grid start dest]
"provides route from start to dest, not including start"
(loop [[current-node & open-nodes] #{start} ;; << throws exception
closed-nodes #{}]
(if (= dest current-node)
[] ;; todo: return route
(let [all-current-neighbours (neighbours-of grid current-node) ;; << returns a set
open-neighbours (set/difference all-current-neighbours closed-nodes)]
(recur (set/union open-nodes open-neighbours)
(conj closed-nodes current-node))))))
When stepping through (with Cider), on the start of the first loop, it throws this exception:
UnsupportedOperationException nth not supported on this type: PersistentHashSet clojure.lang.RT.nthFrom (RT.java:933)
I could use a nested let form that does first/rest manually, but that seems wasteful. Is there a way to get destructured Sets working like this in the loop form? Is it just not supported on Sets?
Sets are unordered, so positional destructuring doesn't make much sense.
According to the documentation for Special Forms, which treats destructuring as well, sequential (vector) binding is specified to use nth and nthnext to look up the elements to bind.
Vector binding-exprs allow you to bind names to parts of sequential things (not just vectors), like vectors, lists, seqs, strings, arrays, and anything that supports nth.
Clojure hash sets (being instances of java.util.Set) do not support lookup by index.
I don't know the context of your example code, but in any case pouring the set contents into an ordered collection, for example (vec #{start}), would make the destructuring work.
As mentioned by others you cannot bind a set to a vector literal, because a set is not sequential. So even this simple let fails with nth not supported:
(let [[x] #{1}])
You could work around this by "destructuring" the set with the use of first and disj:
(loop [remaining-nodes #{start}
closed-nodes #{}]
(let [current-node (first remaining-nodes)
open-nodes (disj remaining-nodes current-node)]
;; rest of your code ...
))
Using (rest remaining-nodes) instead of (disj remaining-nodes current-node) could be possible, but as sets are unordered, rest is in theory not obliged to take out the same element as was extracted with first. Anyway disj will do the job.
NB: be sure to detect remaining-nodes being nil, which could lead to an endless loop.
Algorithm for returning the route
For implementing the missing part in the algorithm (returning the route) you could maintain
a map of paths. It would have one path for each visited node: a vector with the nodes leading from the start node to that node, keyed by that node.
You could use reduce to maintain that map of paths as you visit new nodes. With a new function used together with that reduce and an added nil test, the program could look like this:
(defn add-path [[path paths] node]
"adds a node to a given path, which is added to a map of paths, keyed by that node"
[path (assoc paths node (conj path node))])
(defn navigate-to [grid start dest]
"provides route from start to dest, including both"
(loop [remaining-nodes #{start}
closed-nodes #{}
paths (hash-map start [start])]
(let [current-node (first remaining-nodes)
current-path (get paths current-node)
all-current-neighbours (neighbours-of grid current-node)
open-neighbours (set/difference all-current-neighbours closed-nodes)]
(if (contains? #{dest nil} current-node)
current-path ;; search complete
(recur (set/union (disj remaining-nodes current-node) open-neighbours)
(conj closed-nodes current-node)
(second (reduce add-path [current-path paths] open-neighbours)))))))
The essence of the algorithm is still the same, although I merged the original let with the one needed for destructuring the nodes. This is not absolutely needed, but it probably makes the code more readable.
Test
I tested this with a poor-mans definition of grid and neighbours-of, based on this graph (digits are nodes, bars indicate linked nodes:
0--1 2
| | |
3--4--5
|
6--7--8
This graph seems a good candidate for a test as it has a loop, a dead end, and is connected.
The graph is encoded with grid being a vector, where each element represents a node. An element's index in that vector is the node's identifier. The content of each element is a set of neighbours, making the neighbours-of function a trivial thing (your implementation will be different):
(def grid [#{1 3} #{0 4} #{5}
#{0 4 6} #{1 3 5} #{2 4}
#{3 7} #{6 8} #{7} ])
(defn neighbours-of [grid node]
(get grid node))
Then the test is to find the route from node 0 to node 8:
(println (navigate-to grid 0 8))
Output is:
[0 1 4 3 6 7 8]
This outcome demonstrates that the algoritm does not guarantee a shortest route, only that a route will be found if it exists. I suppose the outcome could be different on different engines, depending on how the Conjure internals decide which element to take from a set with first.
After removing one of the necessary node links, like the one between node 7 and 8, the output is nil.
NB: I found this an interesting question, and probably went a bit too far in my answer.

Is there a better way to dissoc a head or tail range from a sorted-map?

In Java, with a java.util.SortedMap<Long,Object>, we can do something like:
sortedMap.headSet(13).clear()
to get rid of all the elements with keys < 13. I don't see anything similar in clojure.core for clojure's (sorted-map) (which I think is always a clojure.lang.PersistentTreeMap). The best I've come up with is something like this:
(let [clear-up-to 13
sm (sorted-map 1 "aye" 2 "bee" 13 "em" 14 "en")]
clear-keys (take-while #(< % clear-up-to) (keys sm))
(apply dissoc sm clear-keys))
Am I missing something simpler?
Dissoc is the best you can do, but the take-while step should be replaced with subseq or rsubseq, as appropriate.
I don't think there is a more efficient solution in general for the built in sorted-map though using java.util.SortedMap is completely reasonable in many situations. Especially so if you make it into a persistent map after chopping it.
For what it's worth this can also be written with reduce:
user> my-sorted-map
{2 1, 4 3, 6 5, 8 7}
user>  (reduce dissoc my-sorted-map (take-while #(< % 5) (keys my-sorted-map)))
{6 5, 8 7}
though this is mostly a matter of taste. Your example is fine as well.