This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-11-11
Channels
- # adventofcode (1)
- # aleph (1)
- # announcements (3)
- # babashka (39)
- # beginners (84)
- # calva (1)
- # cider (17)
- # clj-kondo (15)
- # cljs-dev (43)
- # clojure (132)
- # clojure-dev (1)
- # clojure-europe (4)
- # clojure-nl (7)
- # clojure-norway (4)
- # clojure-uk (22)
- # clojurescript (56)
- # clojurex (24)
- # cursive (11)
- # data-science (2)
- # datascript (33)
- # datomic (7)
- # docker (2)
- # figwheel-main (11)
- # fulcro (2)
- # jobs (3)
- # joker (29)
- # leiningen (3)
- # nrepl (4)
- # off-topic (11)
- # planck (4)
- # reitit (5)
- # ring (4)
- # shadow-cljs (205)
- # spacemacs (5)
- # xtdb (9)
Hi Is there an easy whey to group the result of a set of functions into an array ? What I want: (deffn validate [param1 param2 param3] (func1 (validationFunc1 param1) (validationFunc2 param2) (validationFunc3 param1 param2 param3)......)) (validate p1 p2 p3) => [validationFunc1Result validationFunc2Result validationFunc3Result] I'm using juxt right now but the fact that I need to provide all paramters to all functions is making my code unmaintenable
If your functions take different arguments, there's not going to be anything simpler than using juxt
with wrappers like I suggested the other day @marcio.akk
I was considering (let [array []] (conj array (validationFunc1 param1) (validationFunc2 param2) (validationFunc3 param1 param2 param3)......) )
or just [ (validationFunc1 param1) (validationFunc2 param2) (validationFunc3 param1 param2 param3) ... ]
@seancorfield Perfect!
Thanks
You can even give them names instead of relying on order in the vector. Will help in maintenance.
{:a (validation-fn-a param1) :b (validation-fn-b param1 param2)}
There must be a prettier way to do this. Original code was (defn get-exam-by-uuid [uuid] (map dissoc-timestamps (repository/get-one-by-uuid uuid))) I needed to run it through one more function, so it became (defn get-exam-by-uuid [uuid] (map add-is-open (map dissoc-timestamps (repository/get-one-by-uuid uuid)))) It works, but is there a better way to apply two functions over a collection than nested maps?
Function composition: comp
user=> (map inc (map #(* % 3) [1 2 3]))
(4 7 10)
user=> (map (comp inc #(* % 3)) [1 2 3])
(4 7 10)
But unfortunately doesn't work in my case. I get "Don't know how to create ISeq from: clojure.core$map$fn__5847".
The major problem here is that I don't know how to debug this code to find out what repository/get-one-by-uuid is returning.
Personally I prefer threading macros for this:
(defn get-exam-by-uuid [uuid]
(->> (repository/get-one-by-uuid uuid)
(map dissoc-timestamps)
(map add-is-open ))
Keep in mind that comp
evaluates right-to-left. Maybe your functions are being applied in the wrong order.
https://clojuredocs.org/clojure.core/comp
if I have a vector of maps like [{:key1 val, :key2 val} {:key1 val, :key2 val} ...]
and I want to turn it into a vector of maps containing a single key like [{:key 1 val} {:key1 val} ...]
, what would be the most idiomatic way to achieve this? I wrote this (map (fn [x] {:key1 (:key1 x)}) [{:key1 1 :key2 2} {:key1 1 :key2 2} {:key1 1 :key2 2}])
and it seems a bit goofy.
right. i'm questioning if you actually have a need. but if you do actually need a map, then just use select-keys
Is there a "prettier" (less verbose) way to do that than (map #(select-keys % [:key1]) vs)
?
Some people prefer to avoid the #() syntax for defining anonymous functions, and might instead prefer (map (fn [m] (select-keys m [:key1])) vs)
Thanks for confirming. It's not the worst, but I'm clearly just not that used to reading such forms (yet) π
You can break out the function in a let
/`letfn` if you find it easier to read that way.
Nah. If there's nothing like that in core, I agree that it'll add more effort than it's worth π
Or if that anonymous function is something they want to use multiple times, to give it a name and use the name there.
Some might even prefer to define that function with a name even if it is only used once -- kinda depends upon preference there -- it can be annoying to pick a reasonable name for something only used once.
another alternative: (map (fn [{v :key1}] {:key1 v}) vs)
that's kind of weird, I guess, but it seems self describing in a way I like
Where at1
returns a function (fn [x] (select-keys m [:key1]))
? You could write at1
as a Clojure macro if you wanted.
Actually, in the case shown, it could be written as a Clojure function, too...
user=> (defn at1 [f & args]
(fn [x]
(apply f x args)))
#'user/at1
user=> (map (at1 select-keys [:a]) [{:a 1 :b 2} {:a 3 :b 4}])
({:a 1} {:a 3})
Creating anonymous functions are a 'core utility', and are more general than at1
, and I think most Clojure programmers familiarize themselves with it in time.
Noted. Thanks @U050ECB92, @U0CMVHBL2
the specter for it is not bad
user=> (sp/select [sp/ALL (sp/submap [:a])] [{:a 1 :b 1} {:a 2 :b 2}])
[{:a 1} {:a 2}]
Could someone help me with a coding example from a book im reading? I need help dissecting what the reduce portion is actually doing, mainly the "let" portion is highly confusing for me lol.
(def order-details
{:name "mitchard blimmons"
:email ""})
(def order-details-validations
{:name
["Please enter a name" not-empty]
:email
["Please enter an email address" not-empty
"Your email address doesn't look like an email address"
#(or (empty? %) (re-seq #"@" %))]})
(defn error-messages-for
"Return a seq of error messages"
[to-validate message-validator-pairs]
(map first (filter #(not ((second %) to-validate))
(partition 2 message-validator-pairs))))
(defn validate
"Returns a map with a vector of errors for each key"
[to-validate validations]
(reduce (fn [errors validation]
(let [[fieldname validation-check-groups] validation
value (get to-validate fieldname)
error-messages (error-messages-for value validation-check-groups)]
(if (empty? error-messages)
errors
(assoc errors fieldname error-messages))))
{}
validations))
(validate order-details order-details-validations)
There are many ways to slice this kind of pie. I would structure this kind of thing slightly differently. Instead of the values in order-details-validators
being vectors with alternating message and predicate, you could instead treat a map with the keys :msg
and :valid?
as a validator and store a vector of these validators as values in that map. Like this:
(def order-details
{:name "mitchard blimmons"
:email ""})
(def order-details-validations
{:name [{:msg "Please enter a name" :valid? not-empty}]
:email [{:msg "Please enter an email address" :valid? not-empty}
{:msg "Your email address doesn't look like an email address"
:valid? #(or (empty? %) (re-seq #"@" %))}]})
You could then define a function that just applies one validator to a given value returning the error message if invalid. Like this:
(defn apply-validator
"Applies a validator to a value and returns an error message if invalid
and nil if valid"
[value {:keys [msg valid?]}]
(when-not (valid? value) msg))
The implementation of validate
no longer require a raw reduce. You can just take the data for which you have validators (using select-keys
), merge it with the validators map through a function that applies all the validators you have (using merge-with
), then keep the stuff which is invalid (using remove
) and pour it into a map (using into
). Like this:
(defn validate
"Returns a map with a vector of errors for each key"
[m validations]
(->> (select-keys m (keys validations))
(merge-with #(keep (partial apply-validator %2) %) validations)
(remove (comp empty? val))
(into {})))
Your original call works as it is:
(validate order-details order-details-validations)
An advantage of doing it this way is, you can add more keys to your validators without breaking anything.^ Sorry for being so wordy π
This piece is destructuring validation
to get at the first two elements of the sequence/vector:
(let [[fieldname validation-check-groups] validation
so it's like (let [fieldname (first validation) validation-check-groups (second validation) ,,,
-- does that help @jkimmerlingAnd the reduce
is relying on treating a hash map (`order-details-validations`) as a sequence -- which has pairs of [key value] [key value]
(whereas the map is {key value key value}
It would be more idiomatic (and probably less confusing?) to use reduce-kv
here:
(reduce-kv (fn [errors fieldname validation-check-groups]
(let [value (get to-validate fieldname)
error-messages (error-messages-for value validation-check-groups)]
,,,
@seancorfield that did help quite a bit, thank you!
@bfabry I'm serious. spectre (spelling?) adds complexity on top of what is essentially a function call
it's its own DSL, an additional dependency, and most people (certainly most of #beginners) don't know it
for the most part I agree with you, I just don't think it's great for the community to express that kind of technical argument the way you did
specter is a load of extra complexity to add to a team/project. unless you happen to be on a team/project that's already happy with it
I can accept that Specter might be "great" for complex, nested data transforms but for a simple case like this it is a) total overkill b) harder to read than all of the core-based suggestions folks made and c) is a whole new learning curve for a declarative (and not very Clojure-y) syntax of its own.
I don't think I've seen a single suggested use of Specter that is directly easier to read than the equivalent core functions so far. Certainly in this channel.
Hi there, please is there a way to temporarily stop logging? Using tools.logging with log back-classic backend, Iβm running simulations on a schedule and my log files are getting enormous. Thanks!
Maybe just:
(binding [clojure.tools.logging/*logger-factory*
clojure.tools.logging.impl/disabled-logger-factory]
(do-stuff))
Hi Alex thanks - what Iβm trying to do is when my simulation starts I disable all logging, and reenable it at the end. Alternatively disable all logging for all threads started by the simulation trade.
Let me try thanks a lot!!
Worked a charm! Thanks so much for the quick answer!
Java methods are not functions. The Math/abs
is syntactic sugar around the .
special form. See https://clojure.org/reference/java_interop
not only that, but also methods are not first class Objects, so cannot be pushed to the stack, so they cannot be provided as arguments
For this particular project there will be something like 300 lessons that we're making. What's a decent way to store lots of maps, a vector with index for each collection of lesson maps?
lots of lessons, each lesson has a vec of maps... with many vecs of maps, what is a smart way to index ?
is there a core function that will take a collection, apply a pred to each item, and produce a collection of something like ([truthy-stuff] [falsy-stuff])
? Essentially filter
that gives you the result and its complement.
in a project.clj, can i have as :resource-paths
all directories except one?
iβd like to lein uberjar
everything in resources/* except one directory that has historically had large files donβt need to be in the jar
looks like no: https://github.com/technomancy/leiningen/blob/0019025b4426da3e84f06c1d34da14e100b8c56c/leiningen-core/src/leiningen/core/eval.clj#L82 assuming iβm looking in the right place