Is there something in core like cond->
that threads the expression value through the test(s) too? I.e. I want to do something like this:
(cond->-> (big-operation-maybe-returns-future!)
future? deref)
Best alternative I could come up with is:
(let [result (big-operation-maybe-results-future!)
result (if (future? result) @result result)]
;; ...
)
could also use cond->
there, but that seems a bit forced
No, but we released a small library at work that has condp->
that does this…
It has condq
(a unary version of condp
) and condp->
/ condp->>
to match cond->
/ cond->>
but thread the expression through the predicate (plus other stuff).
Thanks @seancorfield!
What's the idiomatic way to do the following. I have a huge list of text files I need to process efficiently. Can't keep everything in memory. So I'm going with transducers.
(transduce
(comp (remove #(.isDirectory %))
(map slurp)
(map #(string/split % #"[\n+\?\.]"))
(map (partial map #(string/split % #" +")))
(map (partial map (partial map syllabify-word)))
(map (partial map (partial map (partial map process-syllable)))))
conj
(file-seq (io/file "data-directory")))
What's going on is each step of the transduction creates a new level of a nested sequence. I start with a file, split that on newlines, then split each line of each file on spaces, then split each word of each line of each file into syllables... etc...
The nesting gets confusing to reason about, hard to code complicated transformations for. If I could have everything in memory at one time, I'd just have a flatten step somewhere in there. Is there some simplifying alternative that I'm not seeing?
Edit: I may have just seen an obvious answer. I can combine most of those into a process-file
function which can flatten and do whatever the heck it wants since it will have at most 1 file in memory at a time. Something like:
(defn process-file
[text]
(->> text
(#(string/split % #"[\n+\?\.]"))
(mapcat #(string/split % #" +"))
(mapcat syllabify-word)
(map process-syllable)))
(transduce
(comp (remove #(.isDirectory %))
(map slurp)
(map process-file))
conj
(file-seq (io/file "data-dir")))
Trying to use luminus web framework. After checking the password against the password that's been hashed in the db with buddy.....how exactly do you tell buddy that the user is now logged in and authenticated?
I don't use Luminus or buddy, but usually you do this by setting a cookie with a session id.
I have a map of maps and I want to lookup by a value (value of a particular entry in the inner map), assuming the value is unique and return the inner map and its key as a vector. Is there a more optimal solution for this?
(defn get-user-by-email [users email]
(->> (filter #(= email (::email (last %))) (into [] users))
first))
(get-user-by-email {:one {::first-name "Agatha" ::last-name "Christie" ::email "<mailto:agatha.christie@mail.com|agatha.christie@mail.com>"}
:two {::first-name "Charles" ::last-name "Dickens" ::email "<mailto:charles.dickens@mail.com|charles.dickens@mail.com>"}}
"<mailto:charles.dickens@mail.com|charles.dickens@mail.com>")
Linear search on a collection usually indicates that you ought to look into making your input data more amenable to lookups. For instance, you could here turn the input map into a map of email -> user:
user=> (def users-by-email
(into {}
(map (juxt ::email identity))
[{::id :one ::first-name "Agatha" ::last-name "Christie" ::email "<mailto:agatha.christie@mail.com|agatha.christie@mail.com>"}
{::id :two ::first-name "Charles" ::last-name "Dickens" ::email "<mailto:charles.dickens@mail.com|charles.dickens@mail.com>"}]))
#'user/users-by-email
user=> (users-by-email "<mailto:charles.dickens@mail.com|charles.dickens@mail.com>")
#:user{:id :two, :first-name "Charles", :last-name "Dickens", :email "<mailto:charles.dickens@mail.com|charles.dickens@mail.com>"}
See also https://stackoverflow.com/a/53692195. But if you absolutely need to do a linear search, using reduce
+ reduced
is probably the fastest option: https://stackoverflow.com/a/32405094
use some
(defn get-user-by-email [users email]
(some #(and (= email (::email (last %))) %) users))
@flowthing Yes, in most cases I want to lookup the user details by user-id, but in some cases I dont know the id and need to lookup by email and hence this
@gon Thanks. I am a bit of a newbie. I am wondering how some works on a map of maps. Does it automatically convert the map into a vector of [:key val] ?
Hmm, let's test it in a REPL
Yes, it looks like that. When I did a log of %, I get a vector of the key-value pairs for each entry in the users map. So ‘some’ converts a map of maps into a vector of vectors automatically before applying the predicate
(defn get-user-by-email [users email]
(some #(-> (log %) (and (= email (::email (last %))) %))
users))
(get-user-by-email {:one {::first-name "Agatha" ::last-name "Christie" ::email "<mailto:agatha.christie@mail.com|agatha.christie@mail.com>"}
:two {::first-name "Charles" ::last-name "Dickens" ::email "<mailto:charles.dickens@mail.com|charles.dickens@mail.com>"}}
"<mailto:charles.dickens@mail.com|charles.dickens@mail.com>")
;;output
[:one #:helloworld.restaurant{:first-name Agatha, :last-name Christie, :email <mailto:agatha.christie@mail.com|agatha.christie@mail.com>}]
[:two #:helloworld.restaurant{:first-name Charles, :last-name Dickens, :email <mailto:charles.dickens@mail.com|charles.dickens@mail.com>}]
=> [:two #:helloworld.restaurant{:first-name "Charles", :last-name "Dickens", :email "<mailto:charles.dickens@mail.com|charles.dickens@mail.com>"}]
Thanks everyone for your help
is there just a straight forward way to concat an element to a list?
conj adds to the head, I want it on the tail, I don’t care if it’s slow
concat '() [element]
seems dumb
is there a reason you can't use a vector?
that syntax is less dumb than what you are doing algorithmically
and yeah, usually a vector is the right answer if you want to add at the end
also - why a list? they are very rarely useful
and concat doesn't return one anyway
user=> (type (concat '(1 2) '(3 4)))
clojure.lang.LazySeq
I need it because I am writing a parser
that distinguishes lists from vectors
I could perhaps build it as a vector and when I see the )
convert it to a list
perhaps it distinguishes seqs from vectors? because concat doesn't return a list
and yeah, abstracting the parser internal representation from the concrete clojure data type sounds wise