Hi @manutter51, can you give me some pointers with implementing update-in and assoc-in?
I can try
I’ve been messing around… and came up with
not much really hehe
There’s probably a nice recursive function for that
(def character
{:name "Smooches McCutes"
:attributes {:intelligence 10
:strength 4
:dexterity 5}})
(assoc-in character
[:attributes :intelligence] 19)
; --> {:name "Smooches McCutes",
:attributes {:intelligence 19, :strength 4, :dexterity 5}}
(update-in character
[:attributes :intelligence] + 19)
; --> {:name "Smooches McCutes",
:attributes {:intelligence 29, :strength 4, :dexterity 5}}
; and
(assoc (:attributes character) :intelligence 26)
; --> {:intelligence 26, :strength 4, :dexterity 5}
; stuck here...
;(assoc (assoc :intelligence 26)
I’m trying to write out what happens in assoc, manually using assoc
before trying to write the function
it feels like reduce might work
but first, I want to write it manually for (assoc-in character [:attributes :intelligence] 19)
with just assoc
can you help me with that part?
Ok
thanks
!
so let’s see…
character
is a map, and inside that map is another map, under the key :attributes
and inside the inner map is a key called :intelligence
that you want to change.
genau
(booting up a sandbox repl, just a sec)
I’m here …
(assoc character :attributes (assoc (:attributes character) :intelligence 26))
; --> {:name "Smooches McCutes",
:attributes {:intelligence 26, :strength 4, :dexterity 5}}
Cool,
I think this would be more clear with 3 levels one sec…
Yeah
(def nested-map {:name "bob"
:attributes
{:strengh 10
:dexterity 5
:intelligence {:spatial 50
:verbal 20
:math 53}}})
lol, where have I seen that before 😄
(assoc nested-map :attributes (assoc (:attributes nested-map) :dexterity 100))
; --> {:name "bob",
:attributes {:intelligence {:spatial 50, :verbal 20, :math 53},
:dexterity 100,
:strengh 10}}
(assoc nested-map :attributes (assoc (:attributes nested-map) :intelligence (assoc (:intelligence (:attributes nested-map) ) :spatial 50 ) ) ; doesn't work,... I'm lost in here
(assoc nested-map :attributes (assoc (:attributes nested-map) :intelligence {:a 10 :b 20}))
(assoc nested-map :attributes (assoc (:attributes nested-map) :intelligence (assoc (:intelligence (:attributes nested-map)) :spatial 10 )))
that works
Yay, there you go
(assoc nested-map :attributes
(assoc (:attributes nested-map) :intelligence
(assoc (:intelligence (:attributes nested-map)
:spatial 10))))
more readable
Let’s throw in one more:
(def simple
{:name "Smooches McCutes"
:intelligence 10})
(assoc simple :intelligence 10)
The compare:
(assoc simple :intelligence 10)
(assoc character :attributes
(assoc (:attributes character)
:intelligence 26))
(assoc nested-map :attributes
(assoc (:attributes nested-map)
:intelligence (assoc (:intelligence (:attributes nested-map))
:spatial 60)))
yes those are three good representative cases. There is probably something about nil and identity too
Probably, but we can ignore those for now
Now, could you break that last one down into 3 functions you could call to assoc a new value into the inner :spatial
key?
which last one?
I’m thinking a function to set the :attributes
key, a function to set the :intelligence
key, and one to set the :spatial
key.
the 3rd nested case?
ahh okay
“last one” == (assoc nested-map...)
so like (f :attributes (g :intelligence (h :spatial 10)))
Kinda, except each function is going to need to take the map it’s working with
No global variables allowed 😉
is there a len function?
Since I can’t do this:
(defn my-assoc-in
([m k v] (assoc m k v))
([m [k & ks] v] (println "This is the same arity... bummer.")))
or is ks nil when there is no seq
I’ll check that out
(defn printks [m [k & ks] v] (println ks))
(printks {:a 1 :b 2} :happy "yes")
; --> UnsupportedOperationException nth not supported on this type: Keyword clojure.lang.RT.nthFrom (RT.java:947)
What ever that means
For arrays just use count
vector I mean
“For vectors, just use count
”
bleh is it friday yet?
yup
(defn printks [m [k & ks] v] (println (count ks)))
doesn’t work…I was thinking if count(ks) == 0 then just assoc
otherwise recurse
What arguments are you calling it with?
(printks {:a 1 :b: 2} :happy "yes")
Ok, your :happy
needs to be [:happy]
b/c [[]]
You want to match the signature for assoc-in
, which takes (assoc-in coll ks v ...)
, where ks
is a vector of keys.
I see that’s why assoc-in needs those keys in a vector
if I destructure I can’t count can I?
if I had [col ks v], I could count ks
but with [[k & ks]] ks might not be a vector
is that right?
(defn my-assoc-in
[m [k & ks] v]
(if (empty? ks)
(assoc m k v)
()));recurse stuff here
ks
will always be a vector, but it might be empty
What you’ve got so far looks promising
If you’re having trouble with the “recurse” part, try writing functions assoc-attributes
, assoc-intelligence
, and assoc-spatial
, so that you can combine them to update the :spatial
value.
defn my-assoc-in
[m [k & ks] v]
(if (empty? ks)
(assoc m k v)
(assoc m k (my-assoc-in (get m k {}) ks v))))
woohoo
except,… I didn’t get it by thinking.
just futzing
You’d be surprised how much thinking is futzing. 😉
That’s what makes the repl so useful.
Yeah, I wish I could “see” it though. Maybe I just need a bunch more practice
Thanks for thinking it through with me!
No prob
Seems like update-in
will be like
(defn my-update-in
[m [k & ks] func & args]
(if (empty? ks)
(update m k func args)
(update m k (my-update-in (get m k {}) ks #(func args ???)))))
;; ?? should be something that would get the value
mostly, except args
will be a vector because of the &
, so you’ll need an apply
to spread the args back out into a normal function call
(apply func args)
that is.
ah yes
and whatever ??? it
is
since it seems to pass the value of that last nested key to the function
oh wait, no I’m leading you astray, my bad
You want to just pass that function thru until you get down to your base case
apply will happen on the base case
Right, that’s where you need it
otherwise it’s just func
There’s actually something interesting happening when you add & args
to a recursive function.
something like:
(defn my-update-in
[m [k & ks] func & args]
(if (empty? ks)
(update m k (apply func args (get m k)))
(update m k (my-update-in (get m k {}) ks func))))
Not quite. You need to pass a function to update
Although it would be simpler if you just assoc in your base case
(assoc m k (apply func args (get m k)))
I think that would work.
Your non-base case isn’t passing the args
thru, you’ll need that.
But then you’ll run into something really wacky: every time you recurse, the args get wrapped inside a vector, so if you call (my-update-in m [:a :b :c] f 1 2 3)
, it’s going to get to the base case as (my-update-in m [:c} [[[1 2 3]]])