Fork me on GitHub
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))


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


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


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


it''s really like 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?



🙌 1

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


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


There is no string interpolation happening

Kevin Izevbigie07:08:01

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


Well, clojure doesn't have string interpolation


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


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


I have, and they are actually pretty neat. I sometimes use clojure.core.strint, and prefer it over format in contexts like ClojureScript (where using goog.string.format incurs a page size hit)


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
How to get rid of this warning?


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


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

👍 1

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


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 might be what you're looking for.


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?


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


Yeah, I was thinking cond too and use keywords

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


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


(doc partition-by)

👍 1

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
       (partition-by (fn [x] (or (string? x) x)))
       (map (fn [xs] (if (string? (first xs)) (str/join xs) (first xs))))))


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

Noah Moss18:08:26



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


actually maybe not

Noah Moss18:08:17

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

Noah Moss18:08:19

seems to work?


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
       (partition-by string?)
       (map (fn [xs] (if (string? (first xs)) (str/join xs) xs)))


flatten is frowned upon


even though this is a controlled use

Noah Moss18:08:59

is there a good alternative here?


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
       (partition-by (fn [x] (or (string? x) (rand))))
       (map (fn [xs] (if (string? (first xs)) 
                       (str/join xs) 
                       (first xs))))))


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


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


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 ?


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.


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


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 [ :as json]
            [metasoarous.darkstar :as darkstar]))
and I am getting a ‘not on classpath error’ for darkstar… does anything look off in the above?


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


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!


There’s something off in the darkstar business.


wondering about my metasoarous.darkstar :as darkstar


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


checkout the examples in the readme


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.


the examples show a different namespace name


let's start a 🧵


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…


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.


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

😀 1

this I didn’t know.


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


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


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


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.