Fork me on GitHub
#beginners
<
2021-04-28
>
walterl00:04:57

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)

walterl00:04:58

Best alternative I could come up with is:

(let [result (big-operation-maybe-results-future!)
      result (if (future? result) @result result)]
  ;; ...
  )

walterl00:04:01

could also use cond-> there, but that seems a bit forced

seancorfield02:04:29

No, but we released a small library at work that has condp-> that does this…

seancorfield02:04:46

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).

Eric Ihli02:04:16

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")))

3
👍 5
tom03:04:17

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?

simongray06:04:11

I don't use Luminus or buddy, but usually you do this by setting a cookie with a session id.

Prabu Rajan08:04:10

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 ""}
           :two {::first-name "Charles" ::last-name "Dickens" ::email ""}}
          "")

flowthing08:04:07

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 ""}
            {::id :two ::first-name "Charles" ::last-name "Dickens" ::email ""}]))
#'user/users-by-email
user=> (users-by-email "")
#:user{:id :two, :first-name "Charles", :last-name "Dickens", :email ""}

flowthing08:04:26

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

gon09:04:46

use some

(defn get-user-by-email [users email]
  (some #(and (= email (::email (last %))) %) users))

Prabu Rajan15:04:00

@U4ZDX466T 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

Prabu Rajan15:04:18

@U0ZK920CQ 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] ?

sova-soars-the-sora16:04:07

Hmm, let's test it in a REPL

Prabu Rajan17:04:09

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 ""}
           :two {::first-name "Charles" ::last-name "Dickens" ::email ""}}
          "")

;;output

[:one #:helloworld.restaurant{:first-name Agatha, :last-name Christie, :email }]
[:two #:helloworld.restaurant{:first-name Charles, :last-name Dickens, :email }]
=> [:two #:helloworld.restaurant{:first-name "Charles", :last-name "Dickens", :email ""}]

Prabu Rajan17:04:05

Thanks everyone for your help

grazfather23:04:16

is there just a straight forward way to concat an element to a list?

grazfather23:04:28

conj adds to the head, I want it on the tail, I don’t care if it’s slow

grazfather23:04:18

concat '() [element] seems dumb

em23:04:16

is there a reason you can't use a vector?

noisesmith23:04:37

that syntax is less dumb than what you are doing algorithmically

noisesmith23:04:49

and yeah, usually a vector is the right answer if you want to add at the end

noisesmith23:04:03

also - why a list? they are very rarely useful

noisesmith23:04:16

and concat doesn't return one anyway

user=> (type (concat '(1 2) '(3 4)))
clojure.lang.LazySeq

grazfather23:04:21

I need it because I am writing a parser

grazfather23:04:37

that distinguishes lists from vectors

grazfather23:04:57

I could perhaps build it as a vector and when I see the ) convert it to a list

👍 2
noisesmith23:04:49

perhaps it distinguishes seqs from vectors? because concat doesn't return a list

noisesmith23:04:25

and yeah, abstracting the parser internal representation from the concrete clojure data type sounds wise

grazfather00:04:23

but how? A sentinel value at the head of the vector to say if it’s ‘actually’ a vector? That still seems a bit janky, to me

noisesmith01:04:02

{:data-type :list :contents []}

noisesmith01:04:30

that plus some multi-methods that dispatch on :data-type should get you pretty far

noisesmith01:04:54

alternatively, you can use metadata, but I tend to prefer keeping things explicit and visible

grazfather01:04:01

yeah, fair, I already have my tokenizer like that