Fork me on GitHub
#beginners
<
2021-04-18
>
Oliver04:04:38

I have a vector of hash-maps (def my-data [{:id "1" :value "a"} {:id "2" :value "b"}]). Now I want to lookup an :id and change the respective :value. This does not work: (map (fn[x] (if (= :id "1") (update x :value "c") x)) my-data)

yuhan04:04:59

hint: (= :id "1") always returns the same result

👍 3
Oliver04:04:47

got it. (if (= (:id x) "1") works better 😀

3
Oliver04:04:42

Ok, now I have (map (fn[x] (if (= (:id x) "1") (assoc x :value "c") x)) my-data) which works fine, but is this how you would do it or is there a more concise way?

yuhan05:04:36

I think that looks pretty good, you can look at destructuring map arguments and the for macro to make it more concise

👍 3
yuhan05:04:51

also maybe cond->

👍 3
sova-soars-the-sora05:04:05

You can also write #( %) instead of (fn [x] x) (map #(if (= "1" (:id %)) (assoc % :value "c")) le-data)

sova-soars-the-sora05:04:13

an "anonymous function"

madstap16:04:54

An alternative is to structure your data differently, instead of having a vector of maps (entities), you can have a map from ids to entities, which makes it more convenient to update a specific entity by id. Something like:

(def indexed-data
  {"1" {:id "1", :value "a"}
   "2" {:id "2", :value "b"}})

(assoc-in indexed-data ["1" :value] "c")
The medley library has a function medley/indexed-by that does this transformation, or you can write your own:
(defn index-by [f coll]
  (into {} (map (juxt f identity) coll)))

(= indexed-data (index-by :id my-data)) ;=> true
If you need to keep the order it gets a little more complex, but you could add an :order key to the entity.

🙏 3
Oliver17:04:30

That looks even better as in my real example I use :keywords for the lookup.

tschady13:04:29

https://github.com/redplanetlabs/specter is really good at all these mutations

tschady13:04:41

took some getting used to, but super powerful

Oliver16:04:41

Thanks @U1Z392WMQ but my goal is to understand the basic functions on seqs better - especially for nested structures this is still brainteasing. 😉

sova-soars-the-sora03:04:29

I second the idiomatic

{"key1" {:name "jax"}
 "key2" {:name "jin"}
 "key3" {:name "jan"}}
style, because it's super easy to assoc new maps by giving them an unique key, and easy to update-in, assoc-in, and get-in

sova-soars-the-sora03:04:51

dissoc "key1" also removes the whole entry, quite handy.

Oliver04:04:42

Ok, now I have (map (fn[x] (if (= (:id x) "1") (assoc x :value "c") x)) my-data) which works fine, but is this how you would do it or is there a more concise way?

cpdean11:04:22

if i'm running something in a doseq with a (Thread/sleep ...) between loops, how do I ctrl-c or otherwise interrupt the loop?

jumar18:04:04

Editors like Emacs/Cider has direct support for Ctrl-C. Or maybe you can just wrap it with future and use future-cancel

cpdean19:04:09

so lein repl should not be used on its own?

andy.fingerhut02:04:23

Ctrl-C does not interrupt the loop for you when you run lein repl? What OS are you using, and what is the output of lein version ? Ctrl-C interrupts evaluation for me with macOS 10.14.x and lein version 2.9.5

cpdean11:04:38

this is using lein repl, so it's an nrepl instance

madstap16:04:54

An alternative is to structure your data differently, instead of having a vector of maps (entities), you can have a map from ids to entities, which makes it more convenient to update a specific entity by id. Something like:

(def indexed-data
  {"1" {:id "1", :value "a"}
   "2" {:id "2", :value "b"}})

(assoc-in indexed-data ["1" :value] "c")
The medley library has a function medley/indexed-by that does this transformation, or you can write your own:
(defn index-by [f coll]
  (into {} (map (juxt f identity) coll)))

(= indexed-data (index-by :id my-data)) ;=> true
If you need to keep the order it gets a little more complex, but you could add an :order key to the entity.

🙏 3
Oliver17:04:30

I want to write out (and read) a nested data structure. Basically the EDN format would be perfect, only prn-str writes everything in one line. Is there a way to get a more readable structure (eg. like Firefox does with JSON raw data)?

Oliver17:04:53

I tried pprint but it somehow did not work because the outermost structure was a vector. After changing this to a hashmap (what I wanted to do anyway) now it really looks pretty. 😉

toot 3
pithyless18:04:52

@U7D5724A2 if you're printing large datastructures, you should be aware of clojure.core/*print-length* and clojure.core/*print-level* - if those dynamic vars are bound, you may be surprised when pprint prints something that you can't read back in 🙂

👍 3
pithyless18:04:40

and if you're doing this a lot, fipp is nice and faster than pprint; in fact, it's useful to even set it as the default pretty printer in your CIDER repl, etc.

👍 3
Oliver18:04:16

Will check it out, thank you!

sova-soars-the-sora20:04:58

i want to output nested html for a nested structure.

sova-soars-the-sora20:04:05

good time to use clojure.walk?

seancorfield21:04:40

Sounds more like an application of Hiccup.

sova-soars-the-sora21:04:45

Here's an example input and desired output to get more concrete. ;jars-and-lids-to-html

(def datar [{:jar "hax" :lid "##"} {:jar "snax" :lid "##"} {:jar "slax" :lid "##"}])
desired output:
<html>
  <span class='jar'>hax   <span class='lid'>##</span> </span> 
  <span class='jar'>snax  <span class='lid'>##</span> </span> 
  <span class='jar'>slax  <span class='lid'>##</span> </span> 
</html>

yuhan21:04:05

That looks quite achievable with Hiccup, just map over the collection with hiccup vectors for each element. You can use eg. [:span.jar ...] shorthand for classes

sova-soars-the-sora21:04:36

Brilliant. Thank you! 😃

sova-soars-the-sora21:04:03

kinda like (map (html [:span.jar (:jar %)]) datar) ?

yuhan21:04:43

You generally want just one invocation of the html macro at the top level, so something like (html [:html (for [{:keys [jar lid]} datar] ...)])

sova-soars-the-sora22:04:05

Hmm.. I don't exactly get it but I'll play around with it

yuhan23:04:04

(hiccup.core/html
  [:html
   (for [{:keys [jar lid]} datar]
     [:span.jar jar
      [:span.lid lid]])])
;; => "<html><span class=\"jar\">hax<span class=\"lid\">##</span></span><span class=\"jar\">snax<span class=\"lid\">##</span></span><span class=\"jar\">slax<span class=\"lid\">##</span></span></html>"

sova-soars-the-sora00:04:23

Amazing. Thank you!

jkrasnay11:04:58

Make sure to use hiccup2.core instead of hiccup.core. The latter doesn’t HTML-escape strings by default.

👁️ 2