Fork me on GitHub
#beginners
<
2022-08-01
>
Kevin Izevbigie07:08:01

I am coming from the Javascript world. And what I am really struggling with is how I used string interpolation in Javascript. In clojure, I want to map over a list of domains to pass the domain into the query params. I believe this is correct. But How do I do this with string interpolation? (note - domain-list is a global variable at the moment.)

(def domain-list (flatten (read-csv domain-file)))
(defn find-email []
  (map (fn [domain-list]
         (http/get "example/api/path"
                   {:as :json :query-params
                    {:api_key api
                     :domain domain-list
                     :limit 3}})) domain-list))

rolt07:08:18

(http/get (apply str "example/api/path?api_key=" api "&domain=" domain)) do you mean something like that ?

🙌 1
Kevin Izevbigie07:08:17

mmmm let me test that. That might be it.

rolt08:08:03

i'm not sure why you'd prefer this form, the version you posted is clearer, and should provide escaping

Kevin Izevbigie08:08:29

i guess i do not understand how domain-list is being looped through in my example

rolt09:08:49

(for [domain domain-list] (http/get "..." {:query-params {:domain domain}})) may be clearer ? btw you should rename the argument: (map (fn [domain] (...)) domain-list)

rolt09:08:19

it''s really like array.map in js

Kevin Izevbigie09:08:58

That renaming clears up my confusion. the domain, in your example is like the argument passed to .map in JS?

rolt09:08:19

yes

🙌 1
rolt09:08:40

well to be more precise, mapv would be like array.map, map (and for) returns a lazy seq so you may want to read about that

oddsor11:08:01

For something that looks slightly more like string interpolation you can also look at the format-function

(map (fn [domain]
       (format "" domain))
     ["a" "b" "c"])
=> ("<https://a/index.html>"
    "<https://b/index.html>"
    "<https://c/index.html>")
https://clojuredocs.org/clojure.core/format

hiredman07:08:10

There is no string interpolation happening

Kevin Izevbigie07:08:01

right, my question is, how do I make it happen?

hiredman07:08:32

Well, clojure doesn't have string interpolation

hiredman07:08:19

str can build strings, format is maybe what you would call string interpolation if you weren't used to being able to refer to variables inside strings

Kevin Izevbigie07:08:18

what would be the clojure way of doing this?

Kevin Izevbigie07:08:36

as in, dynamically creating url paths based on a list

hiredman07:08:56

People have written macros that do some kind of interpolation, but I haven't seen them mentioned in a few years, and I have never used them myself

djanus07:08:11

I have, and they are actually pretty neat. I sometimes use clojure.core.strinthttps://github.com/clojure/core.incubator/blob/master/src/main/clojure/clojure/core/strint.clj#L49-L76, and prefer it over format in contexts like ClojureScript (where using goog.string.format incurs a page size hit)

eval202007:08:28

Given a folder with foo.clj containing (ns foo) (defn -main [& args] (prn "Foo")) and deps.edn containing {:paths ["."]} . Running it shows a warning:

$ clj -m foo foo.clj
WARNING: Implicit use of clojure.main with options is deprecated, use -M
"Foo"
How to get rid of this warning?

eval202007:08:14

ah, I got it on the wrong position, simply clj -M -m foo foo.clj works

Michaël Salihi08:08:07

@eval2020 FYI you can remove the filename clj -M -m foo

eval202008:08:38

yes! even better - thanks both 🙏:skin-tone-2:

👍 1
dabrazhe12:08:02

What's the best way to relate values to data range ? eg

1   till 100 is category 1
101 till 120 is category 2
121 till 150 is category 3 .. etc

sheluchin12:08:14

If the ranges are arbitrary and can't be computed then I think any ordered structure would do. A vector of maps like:

[{:cat 1 :start 1 :end 101} ...]
Or a https://clojuredocs.org/clojure.core/sorted-map might be what you're looking for.

dabrazhe12:08:46

They are indeed arbitrary. Perhaps I rephrase the question: if I get a number 105, what's they easiest way to map it to category/number 2?

dabrazhe12:08:40

I was thinking using cond

Martin Půda13:08:11

Maybe something like this:

(defn to-category [n]
  (->> [1 101 121 151]
       (take-while #(>= n %))
       count))

Chase14:08:28

Yeah, I was thinking cond too and use keywords

(defn category [n]                                                                                                        
  (cond                                                                                                                   
    (<= 1 n 100)   :cat-1                                                                                                 
    (<= 101 n 120) :cat-2                                                                                                 
    (<= 121 n 150) :cat-3                                                                                                 
    :else :cat-4))

dabrazhe16:08:58

Thank you, these are all nice solutions

Noah Moss16:08:56

is there an elegant way, given a vector of potentially mixed types, to join consecutive strings while leaving other types as-is? e.g. ["a" "b" "c" {:d :e}] -> ["abc" {:d :e}]

Noah Moss16:08:21

I can do it with a somewhat verbose reduction but I'm wondering if there's an obvious solution I'm missing

ghadi16:08:22

(doc partition-by)

👍 1
ghadi18:08:46

there are many ways to approach this, but I don't think you'll find any super elegant, bc operating on 'ragged' collections is often awkward

Noah Moss18:08:47

@U050ECB92 yeah, thanks, I forgot about that function here's my approach now. not sure if I can make it much better than this

(defn join-strings
  [values]
  (->> values
       (partition-by (fn [x] (or (string? x) x)))
       (map (fn [xs] (if (string? (first xs)) (str/join xs) (first xs))))))

ghadi18:08:57

that's what I was thinking in my head, but you have a bug in it

Noah Moss18:08:26

:thinking_face:

ghadi18:08:32

test case ["a" "b" "c" 42 43 44 "d"]

ghadi18:08:08

actually maybe not

Noah Moss18:08:17

=> ("abc" 42 43 44 "d")

Noah Moss18:08:19

seems to work?

ghadi18:08:19

I didn't read your partition-by predicate clearly

Noah Moss18:08:52

ah no worries

Noah Moss18:08:58

ty for the help 🙏

Noah Moss18:08:44

oh, no I do have a bug

Noah Moss18:08:56

repeat non-string values only keep the first one

Noah Moss18:08:47

i.e. ["a" "b" 41 41]

Noah Moss18:08:47

I think something like this should work instead

(defn join-strings
  [values]
  (->> values
       (partition-by string?)
       (map (fn [xs] (if (string? (first xs)) (str/join xs) xs)))
       flatten))

ghadi18:08:40

flatten is frowned upon

ghadi18:08:46

even though this is a controlled use

Noah Moss18:08:59

is there a good alternative here?

ghadi18:08:07

I like your original partition-by

Noah Moss18:08:22

the issue is that partition-by will partition by consecutive identical predicate values. so when I return the non-string values as-is, the identical ones are put in the same partition. but I ideally want all non-string values to have their own partitions

Noah Moss18:08:42

this is goofy but it technically works lol

(defn join-strings
  [values]
  (->> values
       (partition-by (fn [x] (or (string? x) (rand))))
       (map (fn [xs] (if (string? (first xs)) 
                       (str/join xs) 
                       (first xs))))))

ghadi18:08:36

(defn join-strings
  [values]
  (->> values
       (partition-by string?)
       (mapcat (fn [xs] (if (string? (first xs)) [(str/join xs)] xs)))))

ghadi18:08:09

there's always going to be something irregular, it's inherent in the problem

Noah Moss18:08:45

yep. I like the mapcat version, thanks. I always forget about that function too

Stuart17:08:04

Is their a better way of doing this ?

(let [colors [["a" "foo"] ["b" "bar"] ["c" "quax"] ["d" "mouse"]]]
  (clojure.walk/keywordize-keys (into {} colors)))
I'm thinking something like into that I can pass a function to that tells it how to make the keys ? My other thought was
(let [colors [["a" "foo"] ["b" "bar"] ["c" "quax"] ["d" "mouse"]]]
  (into {} (for [[k v] colors]
             {(keyword k) v})))
Or is this an OK way of doing that ?

hiredman17:08:48

I would do [(keyword k) v] but the result is the same

👍 1
Martin Půda18:08:33

And with update-keys:

(let [colors [["a" "foo"] ["b" "bar"] ["c" "quax"] ["d" "mouse"]]]
  (-> (into {} colors)
      (update-keys keyword)))
@U013YN3T4DA, how did you get colors as a vector of vectors? Maybe you can rewrite that function to produce a hash map directly and avoid that into {} converting.

hiredman18:08:07

(into {} (map (juxt (comp keyword key) val)) colors) or something

macrobartfast18:08:50

this is my deps.edn:

{:paths ["src" "resources" "target/resources"]
 :deps {org.clojure/data.json {:mvn/version "2.4.0"}
        metasoarous/darkstar {:mvn/version "0.1.0"}
        org.slf4j/slf4j-simple {:mvn/version "2.0.0-alpha5"}}}
and this is my require
(ns test.mysite.feat.home
  (:require [clojure.data.json :as json]
            [metasoarous.darkstar :as darkstar]))
and I am getting a ‘not on classpath error’ for darkstar… does anything look off in the above?

ghadi18:08:31

your :deps need to be in a map, they're just loose in the outer map

macrobartfast18:08:36

Oh, copy paste error to here… that’s actually correct in the original… updating this version now to match what’s still throwing the error. Thanks for the catch!

macrobartfast18:08:54

There’s something off in the darkstar business.

macrobartfast18:08:19

wondering about my metasoarous.darkstar :as darkstar

macrobartfast18:08:32

getting this compilation error: failed: Extra input spec: :clojure.core.specs.alpha/ns-form

hiredman18:08:08

checkout the examples in the readme

macrobartfast18:08:47

I had tried that repo (applied-science) as it hadn’t been pushed to clojars, which metasaurus later did and got a different error, but I’ll try again.

hiredman18:08:05

the examples show a different namespace name

ghadi18:08:01

let's start a 🧵

macrobartfast18:08:03

Gotcha… it’s because apparently it was originally fetched from github, but then later pushed to clojars… but appears in the link in metasaurus’s repo… confusing for someone like me, lol…

macrobartfast18:08:27

but I’ll try to use the

applied-science/darkstar {:git/url ""
                          :sha "541a3ff36065c59e92fe6aa61e41a4385ba6f893"}
in my deps.edn again, then I can require it as shown.

hiredman18:08:03

whichever deps coordinate you use, that doesn't effect the names of the namespaces

😀 1
macrobartfast18:08:25

this I didn’t know.

macrobartfast18:08:49

Well, how about them apples!! Thank you!! I had someone gotten into cribbing my requires off the dep in my deps.edn or equivalent.

Noah Moss23:08:42

is there an easy way to split a string around a somewhat complex regex (similar to clojure.string/split) but keep the matches in the output sequence?

Ben Lieberman00:08:30

you mean like re-seq

tomd07:08:12

You can match against a lookaround with clojure.string/split to keep the matches. It's mentioned in the first example on clojuredocs

Noah Moss13:08:15

Is there a way to do this without lookaround, particularly so that it works in ClojureScript as well? The JS lookaround syntax is the same but it's not supported by all browsers

tomd13:08:47

If you can limit it to positive lookaheads, you should be fine afaik. If you really want to avoid them, I guess you could use clojure.string/replace beforehand to insert a character you know won't be in the original string, but it's a bit hacky:

(-> "camelCaseString"
    (str/replace #"(\B[A-Z])" "¬$1")
    (str/split #"¬"))
;; => ["camel" "Case" "String"]

tomd17:08:38

Or you could roll your own fn:

(defn incl-split [string pattern]
  (let [re (re-pattern (str "(.+?)(" pattern ".*)"))]
    (loop [s string
           res []]
      (if-let [[_ match more] (re-matches re s)]
        (recur more (conj res match))
        (conj res s)))))

(incl-split "camelCaseString" #"\B[A-Z]")
;; => ["camel" "Case" "String"]
which you'd need to tailor to your needs. This, for example, wouldn't handle capture groups in the pattern.