Fork me on GitHub
#beginners
<
2022-11-08
>
Jesse Snyder00:11:05

As a complete noob, it surprises me that this raises a NullPointerException rather than raising some other kind of error:

(== nil nil)
Is there a principle at work here, or is this just something that becomes immediately familiar with experience (“oh, right… that”)?

phronmophobic00:11:49

I almost never use == and instead use = for all equality testing.

Jesse Snyder00:11:53

(== 9 9.0) seems like the main use case(?)

Jesse Snyder00:11:34

though I’m guessing you’d often just normalize the types first then use =.

👍 1
Jesse Snyder00:11:00

moving on… 🙂

phronmophobic00:11:27

comparing floats for equality with any kind of number always feels tricky

andy.fingerhut00:11:18

== is for comparing numbers to each other. Its behavior is not defined if you pass it any other type of values.

Jesse Snyder01:11:26

It’s the choice of exception raised that surprised me, but I’m not going to dwell on it!

tschady02:11:46

see (source =) v. (source ==)

slk50007:11:46

(reduce (fn [new-map [key val]] (assoc new-map key (inc val))) {} {:max 30 :min 10}) ;; how to use shorthand? (reduce #([%1 [%2 %3]] (assoc %1 %2 (inc %3))) {} {:max 30 :min 10}) ;; it doesn't work

cheewah08:11:18

(reduce #(let [[key val] %2]
           (assoc %1 key (inc val)))
        {}
        {:max 30 :min 10})

❤️ 1
tvirolai08:11:20

The function shorthand macro doesn't have an argument vector, so the destructuring has to be done either in a let form, as in the example, or skipped:

(reduce #(assoc %1 (key %2) (inc (val %2)))
          {}
          {:max 30 :min 10})

❤️ 1
tomd09:11:22

Or even better for maps:

(reduce-kv #(assoc %1 %2 (inc %3))
           {}
           {:max 30 :min 10})

❤️ 1
tomd09:11:13

(although surely it's more readable without short form syntax?)

slk50009:11:19

you are right it should be written as (fn [new-map [key val]]) for readability, but just wanted to know how to use shorthand

👍 1
Sam Ritchie16:11:15

If you’re on CLojure 1.11, (update-vals {:max 30 :min 10} inc)

❤️ 1
Sam Ritchie16:11:06

and the implementation for comparison to our suggestions

adi10:11:00

x-posting. Might be useful to fellow gentlenerds frequenting this channel.

Lior Neria11:11:25

i have json file that contains vector of jsons i want to save every json as json file in the resources folder in the project anyone can help? example how my json file look like:

[
  {
    "name": "dyyi1"
    "topics": [
      "merge"
    ],  
    "filters": [
      "no"]
  },
{
    "name": "exc"
    "topics": [
      "keeper"
    ],  
    "filters": [
      "no"]
  }]

Ferdinand Beyer11:11:47

Write a small script that uses slurp to read the file, https://github.com/clojure/data.json to parse JSON, then doseq to extract all the sub-objects you want to have and spit them to files again, using data.json for formatting?

👍 1
Ferdinand Beyer11:11:02

Or maybe use jq on the command line

Stuart Nath18:11:42

Hello Everyone, I need help on how to write a function that does the following in idiomatic Clojure: 1. I have a dataset containing item and quantity (already grouped by item, summed by quantity). 2. I have a classification scheme. For example, ABC, where A is the top 50% by qty, B is the next 30%, and C is the last 20%. I would like this scheme to be named var that I pass as a function argument. I would like the function signature to look this: (my-classification dataset classification-scheme) I could do this with a cond statement inside the function "my-classification":

(let [quantity 85]
  (cond
    (<= quantity 50) "A"
    (<= quantity 80) "B"
    (<= quantity 100) "C" 
    :else "D"))
But I don't know how to write the function that can accept a variable number of key-value pairs in the classification scheme data structure that would be used to generate the cond statement.

Brennan C.19:11:34

One way to do something like this would be the following:

(let [quantity 85]
  (->
   (some (fn [[max-quantity category]]
           (when (<= quantity max-quantity) category))
         [[50 "A"][80 "B"][100 "C"]])
   (or "D")))
You can change that vector (`[[50 "A"][80 "B"][100 "C"]]` ) to add more categories. https://clojuredocs.org/clojure.core/some (I use clojuredocs frequently, it's a great resource.

kennytilton19:11:18

"I don't know how to write the function that can accept a variable number of key-value pairs" Do you just need?:

(defn build-scheme [& key-values] 
         key-values)
The & so-called rest operator is the trick.

Stuart Nath20:11:30

Thank you both for your responses. @U03L0CR870T this was exactly what I needed, thank you.

🚀 1
skylize21:11:43

Looking less at the algorithm of the sample scheme, and more at the desired API: Seems tiny wrapper around a map and schemes that destructure key value pairs should set you right up. (Uses condp instead of cond just to expose you to another option.)

(defn abc-scheme [[x n]] ; <- Destructure [k v] pair
  (let [c (condp > n   ; into 2 separate bindings.
            100 "C"    ; <- With logic in your sample,
            80  "B"    ; need to test higher val first
            50  "A"    ; because it shadows other tests.
            "D")]
    [x c]))

(defn classify [m scheme] (into {} (map scheme m)))


(classify {:foo 90
           :bar 60
           :baz 120}
          abc-scheme)
; => {:foo C :bar B :baz D}

Matthew Twomey21:11:19

Just toying with language features and patterns. I am curious is this is an “ok” or “reasonable” way to use multimethods?

(defn round-number-to-nearest [n precision]
  (let [x (/ 1 precision)]
    (-> n
        (* x)
        Math/round
        (/ x)
        float)))

(defmulti round-to-nearest
  "Round number (or string representing a number) to the nearest tenth
  represented (e.g. 1, .01, ...etc) represented by precision"
  (fn [n precision]
    (type n)))

(defmethod round-to-nearest java.lang.Float [n precision] (round-number-to-nearest n precision))
(defmethod round-to-nearest java.lang.Double [n precision] (round-number-to-nearest n precision))
(defmethod round-to-nearest java.lang.String [n precision] (round-number-to-nearest (Float/parseFloat n) precision))

(defn money [n]
  (str "$" (format "%,.2f" (round-to-nearest n 0.01))))

jake09:11:43

Assuming it works (which I haven’t checked), it might be the right thing for your context. A few things jump out at me: • it might be that the surrounding/calling code wants to sort out getting things into their representative type sooner so you don’t have to always take into account the different types the value could be. For example, if it’s a number but you may have got it via IO as a string, consider casting it to a number where you read it • Multi methods shine when you want to do different things based on the dispatch. Taken with my previous point, you would always defer to the same implementation • I think you could collapse the two number ones into :default (look up default multi method if you don’t know what I mean). Possibly with a check and error • Worth considering what behaviour you want for other types like rational, big decimal, big int etc hth Edit: as you said, you’re playing around so maybe my answer isn’t what you’re looking for. Might help someone though!

jake09:11:53

Also, I think the format call might just do exactly what you want in this particular case. Worth trying.

jake09:11:36

Which is to make the wider point: there are many times I’ve started down the multi methods route only to simplify the problem down to not needing to use them after all. Something to look out for when you use them, as they do lead to more code/complexity than not needing to dispatch on type at all. I think they really shine when someone else or perhaps some other area of the codebase can further extend them, perhaps long after the initial implementation.

Matthew Twomey16:11:11

Thanks @U049VPD4BBP - this is exactly what I was looking for. Tell’s me I have the general idea right, but gives me other considerations about how / when / if I might want to actually do something like this. I appreciate your response!

Matthew Twomey16:11:53

I actually revised it a bit for other numbers since I posted that to:

(fn [n precision]
    (if (number? n)
      java.lang.Number
      (class n))))
So that I don’t need the repeated defmethods for different number types.

jake19:11:08

Nice 👍 You can use keywords as the “matchers” (I forget the actual name, if I ever knew it). The reason I say this is that your defmethod can have :number and then if there’s something that doesn’t extend/implement the Java interface/type you can still dispatch to that. You’ll have to decide which makes most sense in your domain. In this case I think java.lang.Number is a good choice as it’s likely all numbery things will extend it, e.g. https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Ratio.java - Sorry for the slightly abstract wordy explanations, on my mobile so can’t really write code easily

Matthew Twomey05:11:25

Oooh cool - thanks @U049VPD4BBP More good “food for thought”. I’m not (yet) using Clojure for work things, I’ve just been enjoying it in the evenings. I’m like learning about stuff like this and how to think more “functionally”.

jake16:11:45

I definitely recommend taking a look at The Joy of Clojure @U0250GGJGAE https://www.manning.com/books/the-joy-of-clojure it goes beyond the the language into how to leverage the features it has. Some really elegant code snippets in there too

Matthew Twomey16:11:24

Nice - thanks @U049VPD4BBP will take a look!