Fork me on GitHub
#beginners
<
2021-10-27
>
phronmophobic00:10:26

I think rewriting a function from kotlin to clojure might be an interesting way to compare, but it probably would be a better comparison if the idea behind the function was already known. One of the issues with this function (imho) is that the algorithm isn't readily apparent. It seems like it's something of the form (into {} (comp (map ..) (filter ..)) props), but it's kind of hard to tell. I'm sure it could be written more cleanly in clojure and probably in kotlin as well.

🙌 2
solf02:10:20

I think trying to rewrite a function from a known language into clojure is a good, honest effort. Albeit probably not the best way to start learning. It’s something everyone has to do at some point or other, and I know I was lost the first time I had to do it. I don’t know kotlin, but here’s a translation anyway, if propertySources was pure data. Probably not a perfect translation, but more to show (somewhat) idiomatic clojure.

👌 2
🙌 2
solf02:10:52

;; update-vals will be added to the next clojure version, but for now:
(defn update-vals [f coll]
  (reduce-kv (fn [m k v]
               (assoc m k (map f v)))
             {}
             coll))

(defn- get-properties-by-prefix-from-source [prefix {:keys [name property-names]}]
  (let [;; sets can be used as a function
        is-env (#{"systemEnvironment" "systemProperties"} name)

        check-prefix (fn [name]
                       (if is-env
                         (str/starts-with? (str/upper-case name)
                                           (str/replace (str/upper-case prefix) "_" "."))
                         (str/starts-with? name prefix)))

        to-key (fn [name] (let [name (if is-env
                                       (str/replace name "_" ".")
                                       name)]
                            (second (str/split name #"\."))))]

    (->> property-names
         (filter check-prefix)
         ;; This will give us a tuple like [key name]
         (map (juxt to-key identity)))))

(defn get-properties-by-prefix [prefix property-sources]
  (->> property-sources
       reverse
       (map #(get-properties-by-prefix-from-source prefix %))
       (apply concat) ;; to flatten

       ;; To convert our list of tuples into a map
       (group-by first)
       (update-vals last)))

(get-properties-by-prefix
 "hello"
 ;; I imagine that's what propertySources could look like if it was pure data
 [{:name "systemEnvironment"
   :property-names ["HELLO_ENV_PROPERTY" "FOO_ENV_PROPERTY"]}
  {:name "configFile"
   :property-names ["hello.config.property" "hello.config.asd"]}])

;; Output

;; => {"config" ("hello.config.property" "hello.config.asd"),
;;     "ENV" ("HELLO_ENV_PROPERTY")}

didibus03:10:13

I can follow this logic haha, and I think you might have gotten it, I was getting too confused by the substring things in the Kotlin one

orpheus03:10:25

Thank you for sharing this!

roelof04:10:08

anyone a idea what the number ** number means here

fabrao10:10:35

Hello, I´ve been using core.match and I want to know if I can set the "matching" rules dynamicaly? Like

(m/match [11]
  [(_ :guard #(> % 10))] 10
  [(_ :guard #(> % 15))] 15
  :else 20)
the (m/match [something] (create-dynamic-rules)) is that possible?

emccue17:10:40

Yes, but you would need to use eval

emccue17:10:08

(eval `(m/match [something] [email protected](create-dynamic-rules)))

emccue17:10:32

so probably not a great idea for “real” code

sheluchin13:10:09

I'm starting to learn how to use D3 from CLJS. Not a whole lot of experience using interop or D3 itself. I'm able to achieve my objective, but could someone please give me some pointers how I could improve my code?

(def data
 [{:letter "A", :frequency 0.08167}
  {:letter "B", :frequency 0.01492}
  {:letter "C", :frequency 0.02782}
  {:letter "D", :frequency 0.04253}
  {:letter "E", :frequency 0.12702}
  {:letter "F", :frequency 0.02288}])

(defn render-barchart [dom-node props]
  (let [svg (-> d3 (.select dom-node))
        data (clj->js props)
        size 200
        ; every item should have the same width
        ; creates a fn that lets you translate the value into a position on x-axis
        x (-> (d3/scaleBand)
              ; start with zero and go to top
              (.rangeRound (into-array [0 size]))
              (.padding 0.1)
              (.domain (into-array js/String (map (fn [d] ^js/String (.-letter d)) data))))
        ; _ (prn (into-array js/String (map (fn [d] (.-letter d)) data)))
        y (-> (d3/scaleLinear)
              ; which min and max value to map into chart
              ; add a little extra to max
              (.domain (into-array [0 (+ 0.15 (apply max (map :frequency data)))]))
              ; start with max value since coords start at top left
              (.range (into-array [size 0])))
        color (d3/scaleOrdinal d3/schemeCategory10)
        selection (-> svg
                    (.selectAll "rect")
                    (.data data #_(fn [d] (.-frequency d))))]
    (-> selection
      .enter
      (.append "rect")
      (.attr "width" (.bandwidth x))
      (.attr "height" (fn [d] (- size (y (.-frequency d)))))
      (.attr "x" (fn [d] (x ^js/String (.-letter d))))
      (.attr "y" (fn [d] (y (.-frequency d))))
      (.attr "fill" (fn [d] (color ^js/String (.-letter d)))))))

Niklas Heer14:10:19

Hi I’m Niklas 👋 and I’m new to Clojure. I wanted to start with using Clojure for my day to day calculations and often I copy some numbers from somewhere which are formatted either in a US (`###,###.###`) or German (`###.###,###`) notation and which can have other symbols like $ or € in the string so I needed to convert those strings into proper numbers Clojure can work with. I searched around and found not really something which solves my problem so I built the following, but now I’m wondering: is there a better way to do this?

(ns playground
  (:import [java.text NumberFormat]
           [java.util Locale]))

(defn nf-us?
  "Checks if the number format is US or not."
  [string]
  (let [[_ fn _] (re-matches #"^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$" string)]
    (if fn true false)))

(defn nf-de?
  "Checks if the number format is GERMAN or not."
  [string]
  (let [[_ fn _] (re-matches #"^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$" string)]
    (if fn true false)))

(defn clean-str
  ; Javascript: /[^\d,.-]/g - strip everything except numbers, dots, commas and negative sign
  ; How to do it: 
  "Cleans up a given integer string to leave only numbers, dots, commas and negative sign."
  [s]
  (apply str (re-seq  #"[\d\,\.\-]+" s)))

(defn str->int
  ; Got it from: 
  ; Locale/US -> 4,974,357.00
  ; Locale/GERMANY -> 4.974.357,00
  "Converts a given string into an integer no matter the number format."
  [string]
  (let [cs (clean-str string)
        nf (NumberFormat/getNumberInstance (cond
                                             (nf_us? cs) (Locale/US)
                                             (nf_de? cs) (Locale/GERMAN)
                                             :else (throw (Throwable. "Unknown number format!"))))]
    (.parse nf cs)))
This is how it behaves:
(comment
  (str->int "4,974,357.1") ; -> 4974357.1
  (str->int "-4.974.357,1$") ; -> -4974357.1)

Niklas Heer14:10:50

I know that the function should probably be named str->float or parse-str or something like that. 😅

Niklas Heer14:10:07

I’m especially concerned with the regex since that is not my strong suit and I got it from a solution for JavaScript. (https://www.codegrepper.com/code-examples/javascript/convert+string+with+dot+or+comma+as+decimal+separator+to+number+in+javascript)

Bob B18:10:10

you might like instaparse - provides the grammar parsing capabilities with just providing a string of a grammar (although the "removing characters that aren't in this set" might still be more straightforward with a regex or just filtering against a set)

Niklas Heer18:10:02

Thank you @U013JFLRFS8 for the recommendation. I’ll look into it 👍 I’ve also seen https://github.com/lambdaisland/regal as an alternative to RegEx. :thinking_face:

Pouya15:10:04

@niklas.heer I'm not sure about "better" but there's a fact: regex sucks! I recently wrote a simple calculator program, and used Antlr4 (https://www.antlr.org) parser generator for parsing the string. It's a bit overkill, but you might want to take a look at it. Here's the source code: https://github.com/pouyacode/calculator-api Here's the live project: https://calc.pouyacode.net I'll be more than happy to help you build the parser for your specific task.

👍 1
Pouya15:10:43

@niklas.heer this project is well-commented. But if you need any help regarding what I did or if you wanted to learn more about Antlr4, we can chat/videocall and talk about it.

Niklas Heer15:10:21

@abbassi.pouya that looks interesting and thank you for your kind offer 🙂 I’ll have a look and if I need help understanding it I’ll take you up on the offer 🙏

Frederico Benevides17:10:18

I created a project with babashka but I want to move some code to it's own lib. I have a doubt on how to deploy this lib to allow to use as a external dependency.

borkdude17:10:09

@fredbene babaskha supports the same dep mechanism as clojure, but in bb.edn instead of deps.edn. You can simply use git deps to get started.

👍 1
borkdude17:10:23

bb.edn:
{:deps {org.your/your-library {:git/url "..." :git/sha "..."}}}

Frederico Benevides17:10:36

Thanks for giving some tip where I need to look.

borkdude18:10:09

Also welcome to ask questions in #babashka

Stuart20:10:02

This is probably a dumb question about git deps... but.. here goes... I see some projects I'd like to try and they say to try them to use like what borkdude says and lists something like

{:deps {org.your/your-library {:git/url "a url" :git/sha "..."}}}
But how do I know what the :git/sha is ?

borkdude20:10:46

@qmstuart you look up the most recent sha on github (or gitlab, etc.) or use a tagged sha

phronmophobic20:10:24

you can pass tags to :git/sha?

borkdude20:10:45

no, but you can look up the SHA on Github releases for example

👍 1
Stuart20:10:56

HOw would I find it on this project? It has no releases and no tags https://github.com/hyperfiddle/rcf

phronmophobic20:10:34

as for picking a commit, just try to find one that looks stable 🤷

Stuart20:10:23

thank you!

👍 1
phronmophobic20:10:56

yea, not sure why they make it so hard to find the git history

borkdude20:10:26

I made a tool called neil which can do this for you: https://github.com/babashka/neil

neil add dep weavejester/medley :latest-sha true
cat deps.edn:
{:deps {weavejester/medley {:git/url ""
                            :git/sha "d723afcb18e1fae27f3b68a25c7a151569159a9e"}}
 :aliases {}}

👍 1
Stuart20:10:48

ah cool! thank you!

Daniel Craig20:10:41

I have a map with keys that are strings and I used map to turn them into keywords; is this the best way to do this?

(let [phonebook {"name" "daniel" "number" "123456789"}]
    (into {} (map (fn [[k v]] {(keyword k) v}) phonebook)))

seancorfield20:10:53

If you're using Clojure 1.11 Alpha 2, you can do (update-keys phonebook keyword)

seancorfield20:10:38

Prior to that, I'd probably use reduce-kv:

(let [phonebook {"name" "daniel" "number" "123456789"}]
  (reduce-kv (fn [m k v] (assoc m (keyword k) v)) {} phonebook))
not because it's shorter but because it won't make a sequence and then reconstruct a hash map.

gratitude 2
seancorfield20:10:07

(we're using Clojure 1.11 Alpha 2 in production, in case folks wonder about working with prerelease versions of Clojure)

Daniel Craig20:10:35

Awesome! Thanks Sean

Daniel Craig20:10:32

oh that looks easy

alexmiller20:10:58

it's not super fast as currently implemented, and will deep transform (which you don't need) but it matches your intent

Daniel Craig20:10:08

The speed shouldn't be too big of an issue, all my maps are small and this will only happen occasionally

Daniel Craig20:10:26

So I'm realizing that my phonebook is not actually a clojure map, but instead a java.util.LinkedHashMap

alexmiller20:10:16

to retain insertion order?

Daniel Craig20:10:46

It's the result of querying a graph database using the Ogre library

alexmiller20:10:00

do you care about retaining the ordering?

alexmiller20:10:23

well several or maybe all of these should still work

alexmiller20:10:35

you'll just get a clojure map at the end

Daniel Craig20:10:49

Yep that's great for my purposes

Apple20:10:02

james bond!

Daniel Craig20:10:15

Nope I'm retired now 😁

Daniel Craig20:10:50

OK I just added an (into {} ... and now it's behaving like I expected! Thanks for the great help!

alexmiller20:10:27

reduce-kv will not work on the LinkedHashMap in Clojure 1.10.x but will now also work as of 1.11.0-alpha2

alexmiller21:10:04

the other two options (into, keywordize-keys) should work in both

gratitude 1
seancorfield21:10:35

dev=> (let [phonebook (doto (java.util.LinkedHashMap.) (.put "name" "daniel") (.put "number" "123456789"))]
 #_=>   (update-keys phonebook keyword))
{:name "daniel", :number "123456789"}
(@alexmiller that's correctly showing update-keys works on a LinkedHashMap? My Java is rusty 🙂 )

alexmiller21:10:37

Would not have in 1.10.3 (if update-keys had existed)

1
seancorfield21:10:51

Clojure 1.10.3
user=> (let [phonebook (doto (java.util.LinkedHashMap.) (.put "name" "daniel") (.put "number" "123456789"))]
(reduce-kv (fn [m k v] (assoc m (keyword k) v)) {} phonebook))
Execution error (IllegalArgumentException) at user/eval136 (REPL:2).
No implementation of method: :kv-reduce of protocol: #'clojure.core.protocols/IKVReduce found for class: java.util.LinkedHashMap
Indeed. I can see that I don't work with LinkedHashMaps very much 🙂

gratitude 1