Fork me on GitHub
#beginners
<
2018-07-12
>
Rachna00:07:36

okay. I'll try that. thank you.

justinlee16:07:47

I have a bunch of code that looks like this (-> rows (add-foo ...) (add-bar ..)) where each of the add functions just does an into on the first parameter. This was working well and easy to read until I wanted to do this: (-> rows (when :flag (add-foo ...) (add-bar ...)). I know these seems trivial, but there is a lot of code like this and it’s inside all kinds of complicated iteration code, so I’d like to maintain the “looks imperative” style. I feel like this should be an easy problem to solve but I can’t crack it. Any ideas?

mg16:07:29

@lee.justin.m looks like cond-> is kind of what you're trying to do there

justinlee16:07:21

hm yea that would work. (i was also trying to figure out something with into + concat and then filter out nils or something like that, but maybe cond-> is all i need)

justinlee16:07:58

it’s a little ugly just because i’m going to have a true condition in most places

justinlee16:07:28

I should probably give the XY: the real reason I’m down this path is because I’m typically looping over some structure, but that structure doesn’t have a 1-to-1 mapping: 1 thing might expand to 2 or 3 things depending on its contents. so I am doing this reduce + into pattern to try to get a flat mapping

mg16:07:52

You can always do something like this:

(-> rows
  (always-add-this)
  (cond-> :flag (sometimes-add-this))
  (always-add-this-too))

dpsutton16:07:32

would the cond look at the truthiness of :flag and not (:flag threaded-value) in that case?

dpsutton16:07:56

so it would always (sometimes-add-this)

justinlee17:07:28

seems to work:

(-> [] (add-a) (cond-> false (add-b)) (cond-> true (add-c)))
=> [:a :c]

noisesmith17:07:48

you don't need cond-> twice there

justinlee17:07:58

well yea but it works 🙂

justinlee17:07:14

i still feel like there’s got to be a solved paradigm for map-over-a-collection where the mapping function is 1-to-many instead of the more typical 1-to-1, but this works

noisesmith17:07:26

sure, mapcat

noisesmith17:07:42

but maybe I don't understand what you mean by "map over a collection" "1-to-many"

noisesmith17:07:11

right, your root issue, use mapcat

noisesmith17:07:33

user=> (mapcat repeat [2 1 0 3 2 0] [:a :b :c :d :e :f])
(:a :a :b :d :d :d :e :e)

noisesmith17:07:01

where in your case it's conditionals and either nil or item, rather than variable number of items

justinlee17:07:24

here’s a super simple example of the basic structure of what I’m doing:

(mapcat process data)
=> (1 nil 2 :even 3 nil)
(defn process [x]
  [x (when (even? x) :even)])
=> #'cljs.user/process
(def data [1 2 3])
=> #'cljs.user/data
(map process data)
=> ([1 nil] [2 :even] [3 nil])
(mapcat process data)
=> (1 nil 2 :even 3 nil)

noisesmith17:07:43

you need to change your function slightly too

noisesmith17:07:01

instead of [x ...], [[x ...]]

noisesmith17:07:11

then it does the same mapping and drops nils

justinlee17:07:16

when i say “1-to-many” i mean that process sometimes needs to kick out 2 elements instead of 1

noisesmith17:07:23

right, mapcat does that

noisesmith17:07:17

oh, so what you actually need is more like (if (even? x) [x :even] [x])

noisesmith17:07:22

then it works with mapcat

justinlee17:07:11

yes that’s the conclusion i came to. but if there are like 10 conditionals in building the vector, that’s not going to work

justinlee17:07:28

i think i should just filter out the nils before returning

justinlee17:07:16

i just thought it’d be nice if there was some paradigm where instead of inserting a nil, i could just do nothing and pass the datastructure along to the next piece of code unchanged

justinlee17:07:47

the cond-> thing actually does accomplish that

Rachna17:07:07

Hello everyone, I have one more issue: This is what I am having as a input. [{:number 1, :name Adam} {:number 2, :name Berry} {:number 3, :name Gary} {:number 4, :name Thomas}] I need to process it based on the following condition: If :number = 2 exist, then print the corresponding :name. Any idea/suggestion how can I process it?

john17:07:15

something like (println (:name (filter #(= 2 (:number %)) your-vec)))

Rachna19:07:00

The solution you suggested are giving me nil. Do you have any idea why? I am really new in clojure. 😕

john19:07:08

Did you see the messages between @U051SS2EU and myself after that? He corrected my code.

john19:07:01

So, you would either call first first, or map over the results of the filter

Rachna19:07:54

this is what you are saying right? (->> myVec (filter #(= 2 (:number %))) first :name)

Rachna19:07:21

This is what I am doing: (println "Name: ", (->> myVec (filter #(= 2 (:number %))) first :name))

Rachna19:07:32

still giving me nil.

john19:07:55

hmm, I'm not getting nils with that, for the vector you previously provided. The println will return a nill though, after it has finished sending the result to standard-out

Rachna19:07:05

@U050PJ2EU Thank you John. It works. It is returning the value to the previous function.

john17:07:39

or (->> your-vec (filter #(= 2 (:number %))) :name)

noisesmith17:07:23

surely map :name and not :name in both of those cases?

john17:07:04

ah my bad.. Should have tested it first 😕 I was thinking ... first :name)

john17:07:36

But yea, if you might have more than one entry with the :number 2, mapping :name would be smarter

jthibaudeau17:07:43

could use

(doseq [itm d]
  (when (= (:number itm) 2)
    (print (:name itm))))

Rachna19:07:45

Thank you very much. this one works but I need to return to value to previous function. but don't know how to return this value. do you have any idea how to return it?

jthibaudeau12:07:06

Sorry I didn't get back to you earlier,

(reduce (fn [prev d] 
          (if (= (:number d) 2) 
            (conj prev (:name d))
            prev))
        []
        d)

jthibaudeau12:07:27

That reduce will return a vector of the names

jthibaudeau17:07:48

where d is your data

jthibaudeau17:07:18

also this is if you only want to print, if you need the return values then my suggestion isn't a good choice

valtteri18:07:12

I’m pondering whether I would a) wrap lots and lots of specs with nilable or b) remove all keys with nil values from my maps before validation.

ghadi18:07:44

The latter is my usual preference

ghadi18:07:34

There's no requirement to spec a raw wire payload. I usually clean up a bit, then apply better simpler specs

valtteri18:07:09

In that case the follow up question: what’s the idiomatic way to do that for nested maps?

valtteri18:07:42

I mean, I’ve seen walk and reduce solutions and written one terrible recursion version myself but I can’t say I feel 100% confident about any of those. I’d like to hear other peoples favourites. 🙂

valtteri18:07:28

Or actually I’d like to hear that there’s something idiomatic in core which does just that.

noisesmith18:07:09

postwalk would be the most general and idiomatic way (checking for map? and removing entries with nil values)

Alex Miller (Clojure team)18:07:11

really, the best path is to avoid ever putting the nils in there in the first place

valtteri18:07:07

Mmm, good point

uwo20:07:44

I’m trying to use the a java.time method DateTimeFormat.parse (https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#parse-java.lang.CharSequence-java.time.temporal.TemporalQuery-), which takes a method reference that would be expressed like LocalDate::from in java. I’m unsure how interop works in this case.

noisesmith21:07:42

LocalDate::from is a syntax to create a java.util.function.Function instance

noisesmith21:07:04

(which internally calls the from method on LocalDate)

hiredman21:07:36

it doesn't take a method reference, it takes a sam

uwo21:07:53

@hiredman thanks!

(def local-date
  (reify TemporalQuery
    (queryFrom [_ ^TemporalAccessor temporal]
      (LocalDate/from temporal))))

(.parse (formatter "MM/dd/uuuu") "03/27/2016" local-date)

uwo21:07:09

still haven’t gotten .parseBest to work, but I’ll get there

sova-soars-the-sora21:07:38

say you're very ambitious and want to make a website that scrapes all the venue websites and band websites for local venues and bands to make a complete calendar of bands & events. what sort of clojure tools for scraping websites are there?

hiredman21:07:32

I've used both htmlunit (which is basically a headless web browser written in java designed for testing websites) and the webdriver rest api thing to drive a headless firefox in a docker container

noisesmith21:07:52

I've had good luck using enlive - it's mainly meant for templating but it worked for me when the page structure was predictable

hiredman21:07:55

htmlunit is much less of a pain, but less likely to work

noisesmith21:07:24

and yeah for pages that require js you'll need one of those browser driven things likely

hiredman21:07:32

if you are lucky, a lot of react based sites these days serve up a big blob of json that you can slurp out and ignore the markup

noisesmith21:07:44

that's handy

sova-soars-the-sora21:07:51

Oh that's an idea. Depending on how nice the data is. wow making a headless browser to process data. what has the world come to haha

sova-soars-the-sora21:07:13

don't know anything about htmlunit. i recall enlive does have some scraping ability now! hmms.

perkele23:07:49

Hi How to wrap select2 or react-select into Rum component?