braveandtrue

https://www.braveclojure.com/
moo 2018-09-14T18:31:10.000100Z

Hi @manutter51, can you give me some pointers with implementing update-in and assoc-in?

2018-09-14T18:31:26.000100Z

I can try

moo 2018-09-14T18:31:27.000100Z

I’ve been messing around… and came up with

moo 2018-09-14T18:31:48.000100Z

not much really hehe

2018-09-14T18:33:16.000100Z

There’s probably a nice recursive function for that

moo 2018-09-14T18:34:57.000100Z

(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)

moo 2018-09-14T18:35:11.000100Z

I’m trying to write out what happens in assoc, manually using assoc

moo 2018-09-14T18:35:18.000100Z

before trying to write the function

moo 2018-09-14T18:35:44.000100Z

it feels like reduce might work

moo 2018-09-14T18:36:08.000100Z

but first, I want to write it manually for (assoc-in character [:attributes :intelligence] 19) with just assoc

moo 2018-09-14T18:36:13.000100Z

can you help me with that part?

2018-09-14T18:36:27.000100Z

Ok

1
moo 2018-09-14T18:36:41.000200Z

thanks

moo 2018-09-14T18:36:42.000200Z

!

2018-09-14T18:36:58.000100Z

so let’s see…

2018-09-14T18:37:56.000100Z

character is a map, and inside that map is another map, under the key :attributes

2018-09-14T18:38:23.000100Z

and inside the inner map is a key called :intelligence that you want to change.

moo 2018-09-14T18:38:45.000100Z

genau

2018-09-14T18:39:42.000100Z

(booting up a sandbox repl, just a sec)

moo 2018-09-14T18:41:09.000100Z

I’m here …

(assoc character :attributes (assoc (:attributes character) :intelligence 26))
; --> {:name "Smooches McCutes",
 :attributes {:intelligence 26, :strength 4, :dexterity 5}}

2018-09-14T18:41:40.000100Z

Cool,

moo 2018-09-14T18:41:46.000100Z

I think this would be more clear with 3 levels one sec…

2018-09-14T18:41:50.000200Z

Yeah

moo 2018-09-14T18:42:02.000100Z

(def nested-map {:name "bob" 
    :attributes 
        {:strengh 10
         :dexterity 5
         :intelligence {:spatial 50
                        :verbal 20
                        :math 53}}})

2018-09-14T18:42:19.000100Z

lol, where have I seen that before 😄

moo 2018-09-14T18:46:20.000100Z

(assoc nested-map :attributes (assoc (:attributes nested-map) :dexterity 100))
; --> {:name "bob",
 :attributes {:intelligence {:spatial 50, :verbal 20, :math 53},
              :dexterity 100,
              :strengh 10}}


moo 2018-09-14T18:51:45.000100Z

(assoc nested-map :attributes (assoc (:attributes nested-map) :intelligence (assoc (:intelligence (:attributes nested-map) ) :spatial 50 ) ) ; doesn't work,... I'm lost in here

moo 2018-09-14T18:52:57.000100Z

(assoc nested-map :attributes (assoc (:attributes nested-map) :intelligence {:a 10 :b 20}))

moo 2018-09-14T18:54:11.000100Z

(assoc nested-map :attributes (assoc (:attributes nested-map) :intelligence (assoc (:intelligence (:attributes nested-map))  :spatial 10 )))

moo 2018-09-14T18:54:12.000100Z

that works

2018-09-14T18:54:20.000100Z

Yay, there you go

moo 2018-09-14T18:55:02.000100Z

(assoc nested-map :attributes 
        (assoc (:attributes nested-map) :intelligence 
               (assoc (:intelligence (:attributes nested-map)  
                        :spatial 10))))

moo 2018-09-14T18:55:04.000100Z

more readable

2018-09-14T18:55:09.000200Z

Let’s throw in one more:

(def simple
  {:name "Smooches McCutes"
   :intelligence 10})

(assoc simple :intelligence 10)

2018-09-14T18:56:24.000100Z

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)))

moo 2018-09-14T18:57:09.000100Z

yes those are three good representative cases. There is probably something about nil and identity too

2018-09-14T18:57:25.000100Z

Probably, but we can ignore those for now

2018-09-14T18:59:05.000100Z

Now, could you break that last one down into 3 functions you could call to assoc a new value into the inner :spatial key?

moo 2018-09-14T18:59:37.000100Z

which last one?

2018-09-14T19:00:03.000100Z

I’m thinking a function to set the :attributes key, a function to set the :intelligence key, and one to set the :spatial key.

moo 2018-09-14T19:00:07.000100Z

the 3rd nested case?

moo 2018-09-14T19:00:18.000100Z

ahh okay

2018-09-14T19:00:19.000100Z

“last one” == (assoc nested-map...)

moo 2018-09-14T19:01:48.000100Z

so like (f :attributes (g :intelligence (h :spatial 10)))

2018-09-14T19:02:54.000100Z

Kinda, except each function is going to need to take the map it’s working with

2018-09-14T19:03:03.000100Z

No global variables allowed 😉

moo 2018-09-14T19:07:50.000200Z

is there a len function?

moo 2018-09-14T19:08:34.000100Z

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.")))

moo 2018-09-14T19:08:57.000100Z

or is ks nil when there is no seq

moo 2018-09-14T19:09:01.000100Z

I’ll check that out

moo 2018-09-14T19:10:55.000100Z

(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)

moo 2018-09-14T19:10:59.000100Z

What ever that means

2018-09-14T19:12:18.000100Z

For arrays just use count

2018-09-14T19:12:23.000100Z

vector I mean

2018-09-14T19:12:33.000200Z

“For vectors, just use count

2018-09-14T19:12:43.000100Z

bleh is it friday yet?

moo 2018-09-14T19:13:13.000100Z

yup

moo 2018-09-14T19:13:27.000100Z

(defn printks [m [k & ks] v] (println (count ks)))
doesn’t work…

moo 2018-09-14T19:13:41.000100Z

I was thinking if count(ks) == 0 then just assoc

moo 2018-09-14T19:13:50.000100Z

otherwise recurse

2018-09-14T19:13:56.000100Z

What arguments are you calling it with?

moo 2018-09-14T19:14:04.000100Z

(printks {:a 1 :b: 2} :happy "yes")

2018-09-14T19:14:35.000100Z

Ok, your :happy needs to be [:happy]

moo 2018-09-14T19:14:51.000100Z

b/c [[]]

2018-09-14T19:15:40.000100Z

You want to match the signature for assoc-in, which takes (assoc-in coll ks v ...), where ks is a vector of keys.

moo 2018-09-14T19:15:51.000100Z

I see that’s why assoc-in needs those keys in a vector

moo 2018-09-14T19:16:55.000200Z

if I destructure I can’t count can I?

moo 2018-09-14T19:17:10.000100Z

if I had [col ks v], I could count ks

moo 2018-09-14T19:17:27.000100Z

but with [[k & ks]] ks might not be a vector

moo 2018-09-14T19:17:30.000100Z

is that right?

moo 2018-09-14T19:20:57.000100Z

(defn my-assoc-in
  [m [k & ks] v] 
  (if (empty? ks)
    (assoc m k v)
    ()));recurse stuff here

2018-09-14T19:23:02.000100Z

ks will always be a vector, but it might be empty

2018-09-14T19:23:54.000200Z

What you’ve got so far looks promising

2018-09-14T19:25:20.000100Z

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.

moo 2018-09-14T19:27:41.000100Z

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))))

moo 2018-09-14T19:27:44.000100Z

woohoo

moo 2018-09-14T19:27:53.000100Z

except,… I didn’t get it by thinking.

moo 2018-09-14T19:28:08.000100Z

just futzing

2018-09-14T19:33:09.000100Z

You’d be surprised how much thinking is futzing. 😉

2018-09-14T19:33:21.000100Z

That’s what makes the repl so useful.

moo 2018-09-14T19:37:02.000100Z

Yeah, I wish I could “see” it though. Maybe I just need a bunch more practice

moo 2018-09-14T19:37:13.000100Z

Thanks for thinking it through with me!

2018-09-14T19:37:20.000100Z

No prob

moo 2018-09-14T19:37:36.000100Z

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

2018-09-14T19:38:51.000100Z

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

2018-09-14T19:39:07.000100Z

(apply func args) that is.

moo 2018-09-14T19:39:41.000100Z

ah yes

moo 2018-09-14T19:40:17.000100Z

and whatever ??? it

moo 2018-09-14T19:40:18.000200Z

is

moo 2018-09-14T19:40:37.000100Z

since it seems to pass the value of that last nested key to the function

2018-09-14T19:41:04.000100Z

oh wait, no I’m leading you astray, my bad

2018-09-14T19:41:35.000100Z

You want to just pass that function thru until you get down to your base case

moo 2018-09-14T19:42:01.000100Z

apply will happen on the base case

2018-09-14T19:42:11.000100Z

Right, that’s where you need it

moo 2018-09-14T19:42:15.000100Z

otherwise it’s just func

2018-09-14T19:43:01.000100Z

There’s actually something interesting happening when you add & args to a recursive function.

moo 2018-09-14T19:43:29.000100Z

something like:

moo 2018-09-14T19:43:32.000100Z

(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))))

2018-09-14T19:44:18.000100Z

Not quite. You need to pass a function to update

2018-09-14T19:44:34.000100Z

Although it would be simpler if you just assoc in your base case

2018-09-14T19:44:58.000100Z

(assoc m k (apply func args (get m k)))

2018-09-14T19:45:06.000100Z

I think that would work.

2018-09-14T19:47:57.000100Z

Your non-base case isn’t passing the args thru, you’ll need that.

2018-09-14T19:50:18.000100Z

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]]])