Fork me on GitHub
#beginners
<
2022-09-01
>
Itay Dreyfus09:09:54

Is there a nice way to map collections that will return a flattened one? So (map vector [1 2 3] [4 5 6]) → [1 4 2 5 3 6]

Ben Sless09:09:11

have you looked at mapcat?

Itay Dreyfus09:09:37

That's better. Thanks! 🙂

Ben Sless09:09:15

This also looks like interleave

Ben Sless09:09:55

(in your specific use case)

Ben Sless09:09:23

but a more general note, you almost never need flatten. I still haven't found one case where it's the appropriate solution, so if you happen to reach for it or something similar, it's almost a hint there's a better more specific solution

1
Itay Dreyfus09:09:58

Good point, thanks!

cvetan13:09:47

hi guys, is there maybe a function that trims character from string but not blank space

cvetan13:09:17

some arbitrary character, or characters?

cvetan13:09:50

if someone worked in php, there is such function which by default trims blank space, but you pass arbitrary characters as well

Martin Půda13:09:16

Dependency:

[org.apache.commons/commons-lang3 "3.12.0"]
(StringUtils/strip "a foo a" "a")
=> " foo "

Ferdinand Beyer13:09:41

You could just use clojure.string/replace with a regular expression:

(str/replace "a foo a" #"^[a]|[a]$" "")

cvetan13:09:27

thank you both for suggestions

skylize14:09:38

You can make your own, using @U922FGW59's suggestion.

(defn trim 
  ([string] (trim "\\s" string))
  ([char string]
  (str/replace string 
               (re-pattern (str "^[" char "]*|[" char "]*$"))
               "")))

(trim (trim "a" "aaa foo   a")) ;; => "foo"

✔️ 1
Jo Øivind Gjernes17:09:17

Is there any json library that keeps ordering ? i.e. enforces array-map?

andy.fingerhut17:09:13

I do not know, but a nitpick: if it did use array-map to preserve ordering of the input key/value pairs, then if in memory you did an assoc on one of those array-map instances, and it had over 8 keys in it, it is nearly certain that it will return a hash-map, with the key/value ordering lost.

andy.fingerhut17:09:42

If you want something that reads json, and then lets you change the data, and spit out json at the end, I would think you would want something that used a data type other than Clojure's built-in array-map. Either that, or you would need to be very strict in how you updated such array-map's if you wanted order to be preserved, e.g. create brand new array-maps from the existing ones, with different key/value pairs.

andy.fingerhut18:09:00

If there were a json library that used this ordered-map data structure in this library, that would make it easier to update ordered-maps without losing the input order: https://github.com/clj-commons/ordered. I do not know if such a json library exists.

ghadi18:09:37

maps don't have order

ghadi18:09:31

full stop. it is not intentional that clojure's array-map preserves order

andy.fingerhut18:09:53

There are places in Clojure's implementation (a very very few) where the fact that array-map preserves order is used explicitly.

ghadi18:09:09

@US3NQ875H what sort of situation are you in where you need map ordering?

pppaul18:09:14

this is a bit common on the frontend, libs are written that consume maps where the order of the keys is important

👍 1
pppaul18:09:55

i have had to patch some libs i use to consume arrays as well...

pppaul18:09:16

i think in cheshire you can have an encoder for a type you make that in clojure looks like a list of mapentries, and cheshire would render that as a json map, preserving your order

Jo Øivind Gjernes18:09:00

Some non-clojure language generates a query result returned in a certain order, making it non trivial to figure out the expect order. Passing through the clojure backend it looks like we’re shuffling results once they contain more than 8 columns 😂

pppaul15:09:10

clojure does array->hash-map promotion after your persistent map gets to be ~8-10 items big. if you are consuming a json map you could write a decoder that returns vectors of mapEntries, thus preserving order and you can write protocol functions to make it a proper type that acts exactly like a persistent map. or tell the producer to stop screwing around and give portable data representations

Damian Koncewicz18:09:45

How can I add 2 numbers from my Atom and pass it to the Sum ?? (defonce app-state (atom {:first-number 0 :second-number 0 :sum 0})) (defn sumNumbers [] #(swap! app-state assoc :sum (+ :first-number :second-number)) ) Or can anyone sent me a link where is article about it. Thank You!!

MegaMatt19:09:56

Is something else responsible for updating first/second number?

MegaMatt19:09:11

i wouldn't personally store the derived state in my atom. It makes it possible to get out of sync which can be a pain. but you could sum them by doing the following.

(defn sumNumbers []
 (swap! app-state update :sum #(+ (:first-number %) (:second-number %)))
editted to remove nested hash function

Damian Koncewicz19:09:43

responsible for numbers have inputs: [:input { :type "number" :class "number-Input" :value (:first-number @app-state) :on-change #(swap! app-state assoc :first-number (-> % .-target .-value)) }] 🙂

Damian Koncewicz19:09:12

And I want to make button to sum it and to show it 🙂

MegaMatt19:09:19

if you use 3 backticks (grave accent) before you paste code it comes across a bit nicer to read

Damian Koncewicz19:09:49

ok I will remember 🙂

Damian Koncewicz19:09:00

[:input 
     { 
      :type "number"
           :class "number-Input"
      :value (:first-number @app-state)
      :on-change #(swap! app-state assoc :first-number (-> % .-target .-value))
      }] 

Ferdinand Beyer19:09:02

(defn sumNumbers []
 #(swap! app-state assoc :sum (+ :first-number :second-number)) )
What's wrong with that? Isn't that exactly what you want?

MegaMatt19:09:45

nope. you need access to the value of :first-number so you need to use update instead of assoc

Chris G19:09:52

update is more inline with his intentions

MegaMatt19:09:55

and then provide an update fn that gets first

Damian Koncewicz19:09:15

I will learn 10000x more from You guys then from internet 🙂

Ferdinand Beyer19:09:20

Ah, now I understand what you mean

Ferdinand Beyer19:09:32

Well @U040ULRJL3B we are guys and girls from the internet

Damian Koncewicz19:09:00

@U922FGW59 You are guys and Girls from Slack 🙂

Martin Půda19:09:11

@U02SD8PATK2 your code

(defn sumNumbers []
 #(swap! app-state update :sum #(+ (:first-number %) (:second-number %)))
throws syntax error, it has nested #()s.

MegaMatt19:09:42

@U040ULRJL3B I would consider just derefing the numbers rather than the sum of the numbers that you synchronize. instead of (let [sum (:sum @app-state)]) do (let [sum (apply + ((juxt :first-number :second-number) @app-state))] or something similar

Chris G19:09:09

might have to be an explicit fn though

(defn sumNumbers []
 #(swap! app-state update :sum (fn [x] (+ (:first-number %) (:second-number %)))

MegaMatt19:09:10

thanks martin. the first #(swap! should just be (swap!

Ferdinand Beyer19:09:15

Tip: Embrace pure functions, and write this one first. And make sure it works:

(update-first-number current-state new-first-number)

Ferdinand Beyer19:09:09

Then you can use that in your event handler: #(swap! app-state update-first-number (.. % -target -value))

Damian Koncewicz19:09:50

Ok I see now I dont get it at all and I try to master making functions tomorrow 🙂 Thank You for Your time!! Have a good night

MegaMatt19:09:13

another option is to add a helper for getting target and just comping it and utilizing the update :first-number

(def evt->val #(-> % .-target .-val)
(partial swap! app-state update :first-number (comp + js/Number evt->val))

😍 1
Damian Koncewicz07:09:22

I made it!!

(defn sum-numbers []                             
   (swap! app-state assoc :sum(->  ( + (edn/read-string(:first-number @app-state )) (edn/read-string(:second-number @app-state ))))))

Damian Koncewicz07:09:51

And quick question is there any good tutorial on the net with ClojureScript? I would like to make some hard stuffs 😛

Martin Půda07:09:37

Is there any reason why you save your numbers as strings? You have to parse them with each call of sum-numbers. I suggest you to use helper by @U02SD8PATK2 and then just this function:

(defn sum-numbers []
  (swap! app-state assoc :sum (+ (:first-number @app-state)
                                 (:second-number @app-state))))

Damian Koncewicz09:09:37

this inputs saves numbers as a string [:input { :type "number" :class "number-Input" :value (:first-number @app-state) :on-change #(swap! app-state assoc :first-number (-> % .-target .-value)) }] I dont know yet why, but they do it. this is my atom: (defonce app-state (atom {:first-number 0 :second-number 0 :sum 0}))

Ferdinand Beyer09:09:22

HTML inputs (text fields) only deal with strings, yes. However, if you want to deal with numbers, it makes sense to convert the text input to a number in the on-change event. This gets more complicated quickly, as you need to validate that the text actually is a number, and decide what to do if not. Typical UI stuff. A good idea might be: • Keep the input text as string • Whenever it changes, try to parse it into a number (integer) • When parsing works: Store the parsed number in the state and do whatever you need to do (sum...) • Otherwise, you can give your user feedback that they need to change their input

Ferdinand Beyer09:09:55

Again, I recommend to break this down into smaller chunks, use pure functions to implement those, test these functions, then put them together. Instead of trying to do everything in one #(swap! ... assoc ...), make your event handler smaller by just calling swap! with a pure function and the current text input:

(defn update-text-input [state text-input]
  ;; This is a _pure_ function. Here you can:
  ;; - Save the text
  ;; - Convert to a number
  ;; - Compute sums
  ;; - ...
  ;; And return the updated state.
  ,,,)

#(swap! app-state update-text-input (.. % -target -value))

Damian Koncewicz13:09:52

@U922FGW59 i found solution:

(defn numberInput [changeValue]
    [:input {:type "number" :value (changeValue @app-state) 
          :on-change (fn [e]
                        (let [new-value (js/parseInt (-> e .-target .-value))]
                          (swap! app-state
                                 (fn [data]
                                   (-> data
                                     (assoc changeValue new-value)
                                     ))
                          )
                        )
                      )
             }
     ]
 )

Damian Koncewicz13:09:17

is there any prettier for clojure in vsc??

Ferdinand Beyer13:09:24

Are you using Calva?

Damian Koncewicz13:09:36

when i have at last line something like this: )))))}] it's looking bad for me, but if it have to be like this I won't complain and just train coding more 🙂

MegaMatt14:09:36

@U040ULRJL3B i highly recommend getting used to the stacked parens and ending syntax. The style you posted above is c style code and you will not miss it when you get used to the stacked parens. I spent most of my 15 years of coding in c style languages and i know it is a bit hard to give up but I think you’ll be glad if you do.

Damian Koncewicz14:09:27

@U02SD8PATK2 Ok i will 🙂 Thank You for Your advice!! 🙂

❤️ 1
Ferdinand Beyer14:09:07

To me it is often also a sign of too complex code if you end up with a bunch of closing brackets

Ferdinand Beyer14:09:26

For instance,

(swap! app-state
    (fn [data]
        (-> data
            (assoc changeValue new-value)
    ))
)
Could be written as: (swap! app-state assoc changeValue new-value) Nesting level 1 instead of 4 🙂

Damian Koncewicz15:09:55

Nice 😄, I took it from tutorial and didn't think it have to be refactored

Ishaan Gupta21:09:12

What usually happens to namespaced keywords outside of a context that supports them? Remove the namespace? {:foo/a 1 :bar/b 2} -> {"a": 1, "b": 2} Keep the namespace as part of the key string? {:foo/a 1 :bar/b 2} -> {"foo/a": 1, "bar/b": 2} Nested context? {:foo/a 1 :bar/b 2} -> {"foo": {"a": 1}, "bar": {"b": 2}} Something else? I don't really know.

seancorfield21:09:48

It depends on what the consumer needs.

seancorfield21:09:40

If you're producing JSON from Clojure hash maps, most of the JSON libraries have settings to control this. We use org.clojure/data.json at work (pure Clojure, zero dependencies, fast enough for most use cases).

seancorfield21:09:04

By default data.json drops the ns:

seanc@Sean-win-11-laptop:~/clojure$ clj -Sdeps '{:deps {org.clojure/data.json {:mvn/version "RELEASE"}}}'
Downloading: org/clojure/data.json/maven-metadata.xml from central
Downloading: org/clojure/data.json/maven-metadata.xml from sonatype
Clojure 1.11.1
user=> (require '[clojure.data.json :as json])
nil
user=> (json/write-str {:foo/bar 1 :foo/quux 2})
"{\"bar\":1,\"quux\":2}"
user=>

Ishaan Gupta07:09:12

Is it better to work with strings of the whole namespace qualified keyword in case you've got a map like {:foo/a 1 :bar/a 2}?

seancorfield15:09:20

Work with whatever is best for the consumer of the data - which use generally keywords within Clojure code.

MegaMatt21:09:21

yep. so for example, if you have necessary context in your namespace such that your data needs it to make sense then you can just convert it to a string in json which is valid.

{
  "filter/name": "some-name",
  "user/name": "some-user",
}
if you had some shape like this it wouldn't work to drop the ns.