Fork me on GitHub
#beginners
<
2021-02-22
>
Cj Pangilinan02:02:54

Is there a difference between update and assoc function? It seems to me they both have the same result.

dpsutton02:02:01

Their doc strings should indicate how they differ. Do you know how to get those?

Cj Pangilinan02:02:02

i use (doc f) while in repl

dpsutton02:02:16

Perfect yeah. So (doc assoc) should say it takes a key and a value. Doc update should say it takes a key and a function

Cj Pangilinan02:02:56

i see. thanks. if you use a function in assoc as in (assoc [] 0 #(str \a)), the value will be the function itself and not the result of executing that function. in update, it will replace the current value with the result of executing the function.

seancorfield02:02:48

@cjpangilinan There's a subtlety there: #(str \a) is a function of no arguments that returns "a" but update requires a function of one or more arguments:

user=> (assoc [] 0 #(str \a))
[#object[user$eval1$fn__2 0x74a9c4b0 "user$eval1$fn__2@74a9c4b0"]]
user=> (update [] 0 #(str \a))
Execution error (ArityException) at user/eval5 (REPL:1).
Wrong number of args (1) passed to: user/eval5/fn--6
user=> (update [] 0 (fn [_] (str \a)))
["a"]
user=>

👍 3
seancorfield02:02:50

The (fn [_] (str \a)) function accepts one argument and ignores it -- _ is idiomatic naming in Clojure for an argument that a function does not use but otherwise has no special meaning.

sb04:02:14

Yes that is interesting. I just tried out ‘(def -str #(str %))’ and ‘(update [] 0 #(-str \a))’ and works.. ofc that isnt good idea

seancorfield04:02:07

@U2T2ZEVPC I don't think you tried exactly that -- it does not work, for exactly the same reason as I observed: #(-str \a) is still a function of no arguments and is not compatible with update:

user=> (def -str #(str %))
#'user/-str
user=> (update [] 0 #(-str \a))
Execution error (ArityException) at user/eval151 (REPL:1).
Wrong number of args (1) passed to: user/eval151/fn--152
user=>

sb06:02:19

I tried and worked. Strange. On mobile Replete REPL.

seancorfield06:02:52

That's ClojureScript -- it may well behave differently than Clojure.

👍 3
seancorfield06:02:11

(but Clojure on the JVM should be considered the "reference")

sb06:02:16

Yes, I see.

seancorfield06:02:58

I would consider it a bug in cljs to allow that invocation, but I suspect it's because JS itself doesn't check function arities strictly?

seancorfield06:02:10

It should also be noted that cljs doesn't have the character data type like Clojure does: in Clojure \a is a single character, in cljs \a is just a single character string "a".

seancorfield06:02:58

(! 733)-> clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "RELEASE"}}}' -M -m cljs.main
ClojureScript 1.10.773
cljs.user=> \a
"a"
cljs.user=> "a"
"a"
cljs.user=> (= \a "a")
true
cljs.user=> ^D

Sun Feb 21 22:18:35
(sean)-(jobs:0)-(~/clojure)
(! 734)-> clj
Clojure 1.10.2
user=> \a
\a
user=> "a"
"a"
user=> (= \a "a")
false
user=> 

👍 3
sb06:02:25

Interesting, I dont know that. Thanks the info. I check these things deeply later today

3
seancorfield06:02:58

Unfortunately, there are lots of small differences between Clojure and cljs (but nowhere near as many now as there used to be!).

👍 3
Cj Pangilinan04:02:59

oh yeah. i got the same error about arity. but i change it to #(str %).

seancorfield04:02:55

@cjpangilinan #(str %) is exactly the same as just str on its own. Well, not quite exactly, since str is variadic -- but both str and #(str %) can be called with a single argument.

👍 3
seancorfield05:02:30

user=> (update [] 0 #(str %))
[""]
user=> (update [] 0 str)
[""]
user=>

Rowan Barnard05:02:05

Hi guys, I just got set up running Calva and Clover in VS Code, just wondering about this workspace concept in VS Code, do I need to bother with that at all or can I just open my Clojure project folder and get cracking?

seancorfield06:02:33

@flyingpython If you're just working with one project -- one folder -- then you can pretty much ignore the workspace stuff.

seancorfield06:02:19

My main use for it is to have a workspace for "work" (with two main projects open) and a workspace for "open source" (with half a dozen active GitHub projects in it). With the former I use Jira and BitBucket, with the latter I use GitHub. The nice thing about the "workspace" idea is that I can switch contexts very easily and VS Code remembers all the files I had open the last time I was using each workspace.

Rowan Barnard06:02:36

Ah OK thank you very much for the info Sean 😉

ajithkumar07:02:24

Hey guys, it might be a noob question! I want to know the difference between defn & defn- also suggest where to use, where not to use! Thanks!

sb07:02:01

https://clojuredocs.org/clojure.core/defn- public or non-public function, check the example

👍 3
ghadi13:02:58

I wouldn't get too invested in making functions private

ghadi13:02:57

a lot of experienced Clojurists don't make too much use of this. You can also hide things in an internal or impl namespace if you really don't want people calling internal functions

👍 3
ghadi13:02:09

this is only a convention, though

andy.fingerhut07:02:19

defn- is the same as defn, except that the function defined is private to the namespace, the same as if you had written (defn ^:private foo [x] ...)

andy.fingerhut07:02:37

Such functions cannot be called from other namespaces, without a special trick. Some Clojure developers like to use private functions to indicate that callers should not call that function as part of the public API of your code. Others prefer to do that via documentation instead, and/or to create a namespace specifically for all public API functions, and leave implementation detail functions for separate namespaces.

Marcus08:02:05

Using core.async channels. Do you know if there are any limitations on the data size per message?

hiredman08:02:15

It as all just pointer sized

sb11:02:33

Is that possible to create faster transducers? I compared few versions, but Im not sure I understand fully the topic. Maybe I did these versions in wrong way https://gist.github.com/damesek/e7023c0bc434a42952128b2f33c702dc? somebody could advise how could I create to create a faster/ better transducer? Thanks

Alex Miller (Clojure team)13:02:45

Your (map inc data) benchmark is not doing the work due to laziness (wrap a doall around that or something)

sb13:02:20

Ok thanks! I check it

Alex Miller (Clojure team)13:02:10

Same for r/map I think - you have to reduce the reducible collection to actually do the work

sb13:02:24

Yes, maybe the map inc isn’t a best test. I re-write later to different task.

Alex Miller (Clojure team)13:02:39

My point is that these transducers are fast, you’re just not getting the correct slow times for sequences

👍 3
sb13:02:03

Ok, I test it. Thank you!

ericdallo14:02:13

Hello! Can anyone help me find a implementation for a function that should replace a text with a new text at specific range?

(update-text "some \ncool\n\n text" "boring" {:start {:line 2
                                                      :column 1}
                                              :end {:line 2
                                                    :column 4}})
=> "some \nboring\n\n text"
The issue seems to be the line/col thing. Not sure it's a beginner question but I could not figure it out how to achive that and I thought this channel could help 😅

andy.fingerhut14:02:45

I don't have a function handy to give you that I know already does this, but if you can take a string s, and any number of {:line <x> :column <y>} maps, and calculate for each one the 0-based index in s where that line and column is, and those indices are start-idx and end-idx, then I think all you need to do after that is (str (subs s 0 start-idx) replacement-string (subs s end-idx)) (perhaps with some off-by-one mistakes there).

ericdallo14:02:29

Thank you for the response! Hum, I think I got your point, it makes sense, I just need to find the start-idx / end-idx like you said. Probably can get that with some for loops

andy.fingerhut14:02:12

Sorry, I gave you the answer for the easy part, not the harder part 🙂

ericdallo14:02:28

Np, any help is welcome 😄 I'm having a hard time with that function haha

borkdude14:02:58

@ericdallo what about something like:

String myName = "domanokz";
String newName = myName.substring(0,4)+'x'+myName.substring(5);
but then in clojure?

borkdude14:02:40

you can first split the original string and then update only the relevant lines

borkdude14:02:52

and then concat them together again with the newlines

ericdallo14:02:53

but what about changes that add more lines? for example

(update-text "some \ncool\n text" "boring\n text" {:start {:line 2
                                                      :column 1}
                                              :end {:line 3
                                                    :column 6}})

borkdude14:02:48

Are you trying to do deltas for clojure-lsp?

ericdallo14:02:04

Yep, incremental text change hahaha

Henri Schmidt14:02:00

working on htis one

Henri Schmidt14:02:03

are lines indexed by 1?

ericdallo14:02:01

Yeah, the line and column start with 1

Henri Schmidt14:02:39

what if the middle contains more than one line?

Henri Schmidt14:02:51

how is the replacement done then?

ericdallo14:02:41

I don't get it, it should behave like a block of code change, you remove a block of code and replace by another

Henri Schmidt14:02:04

ahh okay i see

borkdude14:02:05

but the start and end line + cols always match the replacing input length?

ericdallo14:02:37

oh, no, it matches the old-text

borkdude14:02:30

so maybe an algorithm: cut out the original delta and then concat the new input in between

ericdallo14:02:45

yes, that was @andy.fingerhut suggestion, I'm trying to find the start and end idx so that would work

borkdude15:02:48

@ericdallo

(require '[clojure.string :as str])

(defn offset [lines line col]
  (loop [lines (seq lines)
         buf (java.lang.StringBuffer.)
         idx 0]
    (if lines
      (if (= line idx)
        (+ (count (str buf)) col line)
        (recur (next lines)
               (.append buf (first lines))
               (inc idx)))
      offset)))

(def string "foobar\nbar\nbaz")
(def os (offset (str/split-lines "foobar\nbar\nbaz") 1 1))
(prn (.charAt string os)) ;;=> \a

borkdude15:02:51

Maybe something like this

borkdude15:02:32

Then you can use these offsets, to split the original string into three parts and create the new one from the first and last part + the new one in the middle

borkdude15:02:36

You can optimize this by searching for the next end line and end col by just continuing the loop

borkdude15:02:18

You should take into account Windows newline delimiters, which can be two characters

borkdude15:02:14

The above will probably a bit more performant

ericdallo15:02:18

thanks borkdude, I'll try if the above solution don't work

ericdallo15:02:22

yes, it seems to me too

borkdude15:02:13

Beware of Windows though, should require careful testing

borkdude15:02:26

Always use the file system specific delimiter

ericdallo15:02:09

Yes, had issues with that some time ago in clojure-lsp crawler 😛

Henri Schmidt14:02:36

@ericdallo try this:

(defn update-text [text
                   update-text
                   {{start-line :line start-column :column} :start
                    {end-line :line end-column :column} :end}]
  (let [[prefix middle suffix]
        (->> (clojure.string/split-lines text)
             (map-indexed #(vector %1 %2))
             (partition-by #(or (= (dec start-line) (first %))
                                (= (dec end-line) (first %))))
             (map #(map second %)))
        middle-pre (subs (first middle) 0 (dec start-column))
        middle-suff (subs (last middle) end-column)
        prefix (clojure.string/join "\n" prefix)
        suffix (clojure.string/join "\n" suffix)]
    (-> [prefix middle-pre update-text middle-suff suffix]
        (clojure.string/join))))
    
(update-text "some \ncool\n text" "boring\n text"
             {:start {:line 2 :column 1} :end {:line 3 :column 4}})

ericdallo14:02:50

Damm, that was fast! Thank you!! Let me try

Henri Schmidt14:02:20

It might have an off-by-one error on the columns, but I'm pretty sure it works

Henri Schmidt14:02:50

partition-by is really the key function here that does all the hard work

Henri Schmidt15:02:38

yes there is an error*

ericdallo15:02:41

Really cool!

Henri Schmidt15:02:13

(defn update-text [text
                   update-text
                   {{start-line :line start-column :column} :start
                    {end-line :line end-column :column} :end}]
  (let [[prefix middle suffix]
        (->> (clojure.string/split-lines text)
             (map-indexed #(vector %1 %2))
             (partition-by #(and (<= (dec start-line) (first %))
                                 (>= (dec end-line) (first %))))
             (map #(map second %)))
        middle-pre (subs (first middle) 0 (dec start-column))
        middle-suff (subs (last middle) end-column)
        prefix (clojure.string/join "\n" prefix)
        suffix (clojure.string/join "\n" suffix)]
    (-> [prefix middle-pre update-text middle-suff suffix]
        (clojure.string/join))))
    
(update-text "some \ncool\n text" "boring\n text"
             {:start {:line 2 :column 1} :end {:line 3 :column 4}})

ghadi15:02:56

fyi always require namespace clojure.string explicitly

👍 9
Henri Schmidt15:02:35

yup just wanted to illustrate

ericdallo15:02:44

Thank you! I'll test it with some weird inputs and check if it's working, thank you very much for the help 😄

👍 3
ericdallo15:02:25

It's working but cropping the first col

ericdallo15:02:45

(update-text "some \ncool\n\n text" "boring" {:start {:line 2
                                                      :column 1}
                                              :end {:line 2
                                                    :column 4}})
=> "some boring\n text"

ericdallo15:02:58

it should print "some \nboring\n text"

Henri Schmidt15:02:31

remove (dec start-column)

Henri Schmidt15:02:42

and replace with start-column

ericdallo15:02:26

almost there: "some cboring\n text"

Henri Schmidt15:02:26

wait what was wrong with the dec?

ericdallo15:02:50

it was removing the \n

ericdallo15:02:16

"some boring\n text" should be "some \nboring\n text"

Henri Schmidt15:02:23

yeah so stick a newline in between prefix and middle-pre

Henri Schmidt15:02:28

go back to dec

Henri Schmidt15:02:43

[prefix "\n" middle-pre update-text middle-suff "\n" suffix]

Henri Schmidt15:02:45

sorry i closed my repl

ericdallo15:02:09

it works 😄

👍 3
ericdallo15:02:24

will try multiple inputs, thanks @U01G47XUNHM ❤️

borkdude15:02:00

(require '[clojure.string :as str])

(defn offset [lines line col]
  (loop [lines (seq lines)
         buf (java.lang.StringBuffer.)
         idx 0]
    (if lines
      (if (= line idx)
        (+ (count (str buf)) col line)
        (recur (next lines)
               (.append buf (first lines))
               (inc idx)))
      offset)))

(def string "xxxxxxx\n123\n123")

(defn replace-str [original line col end-line end-col replacement]
  (let [lines (str/split-lines string)
        start-offset (offset lines line col)
        end-offset (offset lines end-line end-col)
        [prefix suffix] [(subs original 0 start-offset)
                                 (subs original end-offset (count original))]]
    (str/join [prefix replacement suffix])))

(prn (replace-str string 1 1 2 1 "dude")) ;;=> "xxxxxxx\n1dude23"

borkdude15:02:08

You can optimize offset by collapsing the finding of the first and second offset into one loop

borkdude15:02:02

Just another possibility ;)

Henri Schmidt15:02:00

definitely more production than mine, i'm fond of code golfing myself

borkdude15:02:24

with similar I mean, it doesn't do the incremental stuff, but I had to fix some stuff related to how it shows diagnostics which also required me to split the lines, etc

👍 3
ericdallo15:02:47

Nice, it looks easier to understand indeed, I wonder about the performance, if it's relevant

borkdude15:02:11

Weren't you starting with this because of performance in the first place? ;)

3
borkdude15:02:32

I would say anything that is executed on each keystroke should be as performant as possible

Henri Schmidt15:02:39

if you're really after performance you should just write a loop with StringBuffer

borkdude15:02:09

I could have sworn I had StringBuffer available in babashka, but that is java.lang.StringBuilder I see now...

borkdude15:02:51

Ah, StringBuilder is more efficient I read now

borkdude15:02:12

but not thread-safe, well, that doesn't matter here

ericdallo15:02:54

@U01G47XUNHM what about texts that don't contain new lines?

(update-text "(+ 1 1)" "2" {:start {:line 1 :column 6}
                            :end {:line 1 :column 7}})

ericdallo15:02:04

I'm getting NPE 😔

borkdude15:02:55

@ericdallo My version returns:

(prn (replace-str "(+ 1 1)" 0 5 0 6 "2" ))
;;=> "(+ 1 2)"

borkdude15:02:03

(it's 0-based)

ericdallo15:02:21

Yeah it works perfectly @U04V15CAJ

ericdallo15:02:39

thank you both, I'll do some tests yet and later a performance check

borkdude15:02:50

@ericdallo on Windows this line (+ (count (str buf)) col line) should probably by incremented by one, if the newline is represented by 2 characters

borkdude15:02:08

no, (+ ... (* 2 line)) even

borkdude15:02:24

I am adding one offset for each line due to the newline char

borkdude15:02:19

maybe LSP already does some normalization on the newlines? don't know

borkdude15:02:20

probably not

ericdallo15:02:26

wait, windows has URI issues related to double \ and etc, why this is related with replacing a text?

borkdude15:02:05

because we are splitting on new line and then trying to find the offset by calculating the length of each line + one for the newline

borkdude15:02:19

but a newline is represented by 2 characters on Windows

ericdallo15:02:43

oh yeah, the \r char... damm

borkdude15:02:44

you can also just split literally on \n which probably also works

borkdude15:02:51

ignoring the \r

borkdude15:02:02

yeah, I think that works

borkdude15:02:26

so you only have to change the str/split-lines part to split on \n

ericdallo15:02:32

But your script is already using string/split-lines that take cares of that, right?

ericdallo15:02:58

on windows it splits by \r\n , got it

borkdude15:02:59

so foo\r\nbar will be foo\r and bar

borkdude15:02:25

this will then calculate the right offset on each OS

borkdude15:02:59

so in this case: totally ignore the file system specific newline :)

ericdallo15:02:05

changed, I'll create a test for that case as well

ericdallo15:02:12

it makes sense

borkdude16:02:45

Here is the more optimized version:

(require '[clojure.string :as str])

(defn offsets [lines line col end-line end-col]
  (loop [lines (seq lines)
         buf (java.lang.StringBuilder.)
         idx 0]
    (when lines
      (if (= line idx)
        [(+ (count (str buf)) col line)
         (loop [lines lines
                buf buf
                idx idx]
           (when lines
             (if (= end-line idx)
               (+ (count (str buf)) end-col end-line)
               (recur (next lines)
                      (.append buf (first lines))
                      (inc idx)))))]
        (recur (next lines)
               (.append buf (first lines))
               (inc idx))))))

(defn replace-str [original line col end-line end-col replacement]
  (let [lines (str/split-lines original)
        [start-offset end-offset] (offsets lines line col end-line end-col)
        [prefix suffix] [(subs original 0 start-offset)
                         (subs original end-offset (count original))]]
    (str/join [prefix replacement suffix])))

(prn (replace-str "xxxxxxx\n123\n123" 1 1 2 1 "dude")) ;;=> "xxxxxxx\n1dude23"
(prn (replace-str "(+ 1 1)" 0 5 0 6 "2" )) ;;=> "(+ 1 2)"

borkdude16:02:38

It collapses the finding of both offsets into one loop

hyper-clap 3
ericdallo16:02:09

Using that works too 🙂 Thank you @U04V15CAJ will follow with that

borkdude16:02:58

@ericdallo 2 improvements: - don't use StringBuilder, it isn't necessary - don't use OS specific newline delimiter

(require '[clojure.string :as str])

(defn offsets [lines line col end-line end-col]
  (loop [lines (seq lines)
         offset 0
         idx 0]
    (when lines
      (if (= line idx)
        [(+ offset col line)
         (loop [lines lines
                offset offset
                idx idx]
           (when lines
             (if (= end-line idx)
               (+ offset end-col end-line)
               (recur (next lines)
                      (+ offset (count (first lines)))
                      (inc idx)))))]
        (recur (next lines)
               (+ offset (count (first lines)))
               (inc idx))))))

(defn replace-str [original line col end-line end-col replacement]
  (let [lines (str/split original #"\n") ;; don't use OS specific line delimiter!
        [start-offset end-offset] (offsets lines line col end-line end-col)
        [prefix suffix] [(subs original 0 start-offset)
                         (subs original end-offset (count original))]]
    (str/join [prefix replacement suffix])))

(prn (replace-str "xxxxxxx\n123\n123" 1 1 2 1 "dude")) ;;=> "xxxxxxx\n1dude23"
(prn (replace-str "(+ 1 1)" 0 5 0 6 "2" )) ;;=> "(+ 1 2)"

ericdallo16:02:46

Nice, mine is just missing the StringBuilder removal so

ericdallo02:02:04

@U04V15CAJ For some reason your function is failling for some cases, I could only reproduce with that test:

(is (= "(+ 1 1)\n" (#'f.file-management/replace-text "(+ 1 1)\n" "\n" 1 0 1 0)))
It gives a NPE Not sure it's the only issue though

borkdude10:02:25

I'll take a look

borkdude11:02:49

@ericdallo

(require '[clojure.string :as str])

(defn offsets [lines line col end-line end-col]
  (loop [lines (seq lines)
         offset 0
         idx 0]
    (if (or (not lines)
            (= line idx))
      [(+ offset col line)
       (loop [lines lines
              offset offset
              idx idx]
         (if (or (not lines)
                 (= end-line idx))
           (+ offset end-col end-line)
           (recur (next lines)
                  (+ offset (count (first lines)))
                  (inc idx))))]
      (recur (next lines)
             (+ offset (count (first lines)))
             (inc idx)))))

(defn replace-str [original line col end-line end-col replacement]
  (let [lines (str/split original #"\n") ;; don't use OS specific line delimiter!
        [start-offset end-offset] (offsets lines line col end-line end-col)
        [prefix suffix] [(subs original 0 start-offset)
                         (subs original end-offset (count original))]]
    (str/join [prefix replacement suffix])))

(prn (replace-str "xxxxxxx\n123\n123" 1 1 2 1 "dude")) ;;=> "xxxxxxx\n1dude23"
(prn (replace-str "(+ 1 1)" 0 5 0 6 "2" )) ;;=> "(+ 1 2)"
(prn (replace-str "(+ 1 1)\n" 1 0 1 0 "\n")) ;;=> "(+ 1 1)\n\n"

borkdude11:02:05

Also notes that this now works:

(prn (replace-str "(+ 1 1)\n" 0 3 0 3 "1 ")) ;;=> "(+ 1 1 1)\n"

ericdallo12:02:16

Thanks! I'll try

Henri Schmidt15:02:17

Updated version

Henri Schmidt15:02:19

@ericdallo you maintain emacs-LSP?

Henri Schmidt15:02:31

That's awesome tooling I thank you for that

ericdallo15:02:45

clojure-lsp, Yes #lsp 😄

Henri Schmidt15:02:01

Even though I use CIDER 😁

cider 3
ericdallo15:02:17

I use both 😛

Henri Schmidt15:02:31

that's high level, i don't code too much anymore

Henri Schmidt15:02:02

will have to try when i have the chance

❤️ 3
Pablo17:02:20

Hello everyone I’m creating a RESTful API by using metosin/compojure-api (specifically compojure.api.sweet/api), but I have a little problem. I want to censor some keys in every response. I added a middleware by using the extra middleware vector (`:middleware` key), but it is not applied on the default exceptions handlers. I saw the code of [`compojure.api.middleware/api-middleware`](https://github.com/metosin/compojure-api/blob/5c88f32fe56cdb6fcdb3cc506bb956943fcd8c17/src/compojure/api/middleware.clj#L213) and I found that middlewares are wrapped by these exceptions handlers. Could you give me a suggestion of how can I define a middleware that applies on every response? I tried to apply it after api, but it returns an InputStream on the body. Thanks!

grazfather17:02:09

I have a let that includes an api call. What’s the ‘right’ way to bind in a let that doesn’t mess up if one of the args (the api call response) is nil?

(let [inside-temp (-> (read-reg bus reg-temp)
                  (get 1)
                  bytes->val
                  reading->C)
            outside-temp (get-manhattan-temp) ; can be nil
            now (java.time.LocalDateTime/now)
            min-temp (minimum-legal-temp now outside-temp)]

grazfather17:02:38

I could do a when and then another let to bind min-temp but that seems ugly

borkdude17:02:12

@grazfather You can do min-temp (when outside-temp (minimum-legal-temp ...))

borkdude17:02:26

or use a default temp outside-temp (or (get-man...) -1)

borkdude17:02:01

You can also use some-> and some->> for short-circuiting on nil

grazfather17:02:13

ok cool, and is when outside-temp better thn when (not (nil? outside-temp?

borkdude17:02:50

assuming that outside-temp is a number or nil, you can just use (when outside-temp ..)

borkdude17:02:38

it might nice to short-circuit the whole thing with when-let on get-manhattan-temp so you don't do any work that's not necessary

grazfather18:02:06

ah, I did not know about when-let

nozd19:02:28

what did i do wrong here? Can someone help? I’m expecting ["1" "2"] but only getting an empty array

(let [ids []]
    (doseq [jdoc ({:id "1" :data "x"} {:id "2" :data "y"})]
      (conj ids (:id jdoc)))
    ids)

Carlo19:02:25

@UJ5N28CL8 that happens because (conj ids (:id jdoc)) is just returning the new seq, but never updating in place your ids binding.

Pablo19:02:27

Remember that things are inmutable on Clojure

Pablo19:02:23

So conj returns a new collection

Carlo19:02:43

you're probably looking for something along the line of:

(map :id [{:id "1" :data "x"} {:id "2" :data "y"}])

nozd19:02:55

thanks guys, yea that’s definitely the problem

Henri Schmidt19:02:48

@grazfather I think you should take a look at if-let but if that doesn't satisfy your use case, I recommend taking a look at the following macro: https://github.com/schmidt73/guidescan-web/blob/master/src/guidescan_web/utils.clj

grazfather02:02:11

I ended up using a very similar when-let*

Henri Schmidt19:02:13

if-let* binds forms but short circuits to the else branch if any of the bindings are nil.

Henri Schmidt19:02:08

It's very useful in general and I think there was some debate whether it should be included in the clojure.core

Henri Schmidt19:02:50

In your case you would simply replace your let with if-let*