Fork me on GitHub
#beginners
<
2017-06-22
>
victora00:06:45

Is there any way I can access the rest of the collection when doing a reduce?

victora00:06:56

Like, the part of the collection that reduce not yet reached

victora00:06:37

Also, is there someone that can help me bringing a method from python to clojure? I can't seem to do it

donyorm00:06:57

I can try and help with moving the python method, @victora

donyorm00:06:07

I don't know about the reduce issue

noisesmith00:06:16

you could make the collection part of the accumulator, but I think it would be easier to use loop instead if you need to access the full collection on each iteration

noisesmith00:06:51

if you only need a specific part - eg. the very next item, there’s other simpler things

noisesmith00:06:37

(reduce (fn [acc [x y]] ...) {} (partition 2 1 coll)) - each step sees the next value in line

noisesmith00:06:16

only problem with that is the sequence is one short

noisesmith00:06:53

(defn each-pair [coll] (partition 2 1 (concat coll [nil]))) is one approach

donyorm00:06:36

Does anyone know how to print a stack trace from within any function (not on a repl)

noisesmith00:06:46

in 1.8 forward (prn e) on an exception shows the stack trace

noisesmith00:06:12

there’s also the .printStackTrace method, that can be called on exceptions, and .getStackTrace which can be called on threads

victora00:06:42

Ok, first things first: Can I reduce over a map? If so, the element given is the pair (key, value)?

noisesmith00:06:12

there’s even a dedicated reduce-kv for this which gives the key and value as separate args

victora00:06:22

The thing about the code I'm trying to do is, it has many if/elses. With the reduce passing many arguments, the thing is horrible to read

victora00:06:27

That can't be the right way lol

donyorm00:06:00

can you post the code in a snippet?

victora00:06:24

I'm finishing it then yes sure, one minute

victora00:06:01

How can I use apply to sum attributes inside a map

victora00:06:13

like, list of maps, and those maps have the attribute :amount

victora00:06:06

apply and map?

donyorm00:06:10

@victora I think this works (repl-tested) (apply + (map :attribute attr-map))

madstap01:06:01

Is vals on a sorted map guaranteed to return them in order?

seancorfield01:06:08

Also (transduce (map :attribute) + attr-map)

seancorfield01:06:19

Or (reduce + (map :attribute attr-map))

dpsutton01:06:57

@madstap i believe that it does. underneath it calls seq on it which should obey the sorted map

seancorfield01:06:06

@madstap vals returns elements in the same order as seq on the map so, yes.

victora01:06:15

How can I make this less ugly?

(defn get-periods-of-debt [account]
  (create-entry-if-necessary account)
  (json/write-str ((reduce 
                     (fn [{:keys [report old-balance]} k v]
                             (let [current-balance (apply + (map :amount v))
                                   current-date k]
                               (if (< old-balance (bigdec 0.0))
                                 (if (>= current-balance (bigdec 0.0))
                                   {:report (assoc report (- (count report) 1) :end current-date)
                                    :old-balance current-date}
                                   {:report (conj (assoc report (- (count report) 1) :end current-date)
                                                  {:principal current-balance
                                                   :start current-date})
                                    :old-balance current-date})
                                 (if (>= current-balance (bigdec 0.0))
                                   {:report report
                                    :old-balance current-balance}
                                   {:report (conj report 
                                                  {:principal current-balance
                                                   :start current-date})
                                    :old-balance current-date}))))
                     {:report []
                      :old-balance (bigdec 0.0)}
                     (group-by-date (all-operations account))) :report)))
@donyorm

donyorm01:06:01

I'm not the best at making things elegant, let me see what I can find

donyorm01:06:29

Could you give me an expected parameter (some dummy value) @victora so I can test the output

donyorm01:06:20

My computer is about to die, so we'll see if I make it to an answer 🙂

victora01:06:11

I'll try to get one, give me one second

donyorm01:06:18

It actually looks pretty good

donyorm01:06:03

As I said, I'm not the best at reducing things to the minimum code, but this about what I would do. And my computer just died. Maybe someone else can take a look too. Great job!

akiroz02:06:25

@victora what does the output of group-by-date look like?

akiroz02:06:31

this code probably doesn't do what you intended, there's an extra pair of paren around reduce & the reducing fn signature should look something like this: (fn [{:keys [report old-balance]} [date records]])

victora02:06:49

It was supposed to be a reduce-kv

victora02:06:55

That indeed was a mistake

victora02:06:46

group-by-date is a map where the keys are dates and the values are lists of operations. An operation is a map {:amount, :date, :description, :id}

akiroz02:06:05

let me see if I could work something out....

victora02:06:20

I changed things a bit and it looks a bit better, but It may still be bugged:

(defn get-periods-of-debt [account]
  (create-entry-if-necessary account)
  (json/write-str ((reduce-kv
                     (fn [{:keys [report old-balance]} k v]
                       (let [current-balance (+ old-balance (apply + (map :amount v)))
                             current-date k
                             last-i (- (count report) 1)]
                         {:old-balance current-balance
                          :report (if (< old-balance (bigdec 0.0))
                                    (if (>= current-balance (bigdec 0.0))
                                      (assoc report last-i (assoc (report last-i) :end current-date))
                                      (conj (assoc report last-i (assoc (report last-i) :end current-date))
                                            {:principal current-balance
                                             :start current-date}))
                                    (if (>= current-balance (bigdec 0.0))
                                      report
                                      (conj report 
                                            {:principal current-balance
                                             :start current-date})))}))
                     {:report []
                      :old-balance (bigdec 0.0)}
                     (group-by-date (all-operations account))) :report)))

victora02:06:50

It may also be correct, but is still ugly

akiroz02:06:15

I'm on a long train ride, great exercise for my brain 😛

victora02:06:38

Making code concise and elegant is def good exercise. Plus points if it's python 🙂

devrandomguy02:06:39

Is it possible to dispatch a multimethod on the first argument? Something like:

(defmulti cmd (fn [k & _] k))
(defmethod cmd :a [x y] (+ x y))
(defmethod cmd :b [x y] (* x y))

(cmd :a 1 2)

devrandomguy02:06:11

The problem I'm having, is that dispatch function is complaining about being passed too many args. But it should be able to handle any number of args, and return only the first, no?

((fn [k & _] k) 1 2 3)  ;; 1

akiroz02:06:57

@victora this is as far as I got, i'm getting off now:

(defn get-periods-of-debt [account]
  (create-entry-if-necessary account)
  (->> (group-by-date (all-operations account))
       (reduce-kv
         (fn [acc date operations]
           (let [current-balance (apply + (map :amount operations))]
             (cond [(<  old-balance     0)  
                    (>= current-balance 0)] 
                   [true  true]   (update acc :reports #(conj ...))
                   [true  false]  
                   [false false]  
                   acc)))
         {:reports      []  
          :old-balance  0}) 
       :reports
       json/write-str))

victora12:06:42

akiroz: that's way better, I didn't know cond. Thanks!

mattgeb03:06:04

Hrm, following this tutorial: https://github.com/Day8/re-frame/blob/master/docs/CodeWalkthrough.md but when I go to http://localhost:3449/example.html the app doesn't load, it just says "Reagent example app – see README.md"

victora12:06:02

What's the elegant way to code such operation?

user=> (let [s {:a {:b {:c 1}}}]
  #_=>   (assoc s :a (assoc (s :a) :b (assoc ((s :a) :b) :c 3))))
{:a {:b {:c 3}}}
Thanks!

Alex Miller (Clojure team)12:06:58

(assoc-in s [:a :b :c] 3)

victora14:06:55

Is there any way I can do cond with two conditions?

victora14:06:01

To get all combinations of true/false

cpoix14:06:26

(let [code (+ (if cond1 1 0) (if cond2 2 0))]
   (cond (= code 0) (println "false false")
             (= code 1) (println "true false")
             (= code 2) (println "false true")
             (= code 3) (println "true true")))
was it your question ?

victora14:06:57

Yes, correct! A bit of a workaround though

cpoix14:06:03

yes awful indeed, you can hide it in macro 😉

victora14:06:17

How can I do that master?

tap14:06:32

What about

(case [cond1 cond2]
  [true false] :a
  [true true] :b
  [false true] :c
  [false false] :d)
?

victora15:06:00

tap: Let me try that

victora15:06:58

Wonderful!

Alex Miller (Clojure team)14:06:25

(if cond1 (if cond2 :a :b) (if cond2 :c :d))

victora15:06:55

alexmiller: I don't find that quite elegant, since I have to write cond2 twice. But it's what I'm using

tap15:06:52

Yeah, my approach has a flaw as we can't use nil as false

tap16:06:12

Or more verbose

(case (mapv boolean [cond1 cond2])
  [true false] :a
  [true true] :b
  [false true] :c
  [false false] :d)

Alex Miller (Clojure team)16:06:15

you can if you wrap cond1 and cond2 in boolean

Alex Miller (Clojure team)16:06:35

oh, sorry you’ve got that :)

zlrth16:06:31

i’m not able to import cljs.spec in a clojurescript file and i can’t figure out why.

clojure.lang.ExceptionInfo: No such namespace: cljs.spec, could not locate cljs/spec.cljs, cljs/spec.cljc, or Closure namespace "cljs.spec" in file src/flierplath/db.cljs
declaration in db.cljs:
(ns flierplath.db
    (:require [cljs.spec :as s]))
i have recent versions of clojure/script:
[org.clojure/clojure "1.9.0-alpha10"]
[org.clojure/clojurescript "1.9.562"]

zlrth16:06:13

aw man. let me check that out.

zlrth16:06:25

works! much appreciated, @tap

okwori17:06:36

Hi guys, any volunteers willing to get online with me for a couple of minutes and explain how to implement core.async on an API call am making so I can use the concurrency and make it faster?

eriktjacobsen18:06:16

I think the reason you're having trouble mocking this to core.async is that your code is somewhat confusingly structured because its assuming coll in many places

eriktjacobsen18:06:46

I'd re-write your functions to operate on a single element at a time, only mapping over colls at a higher level, then you'd be able to work them into core.async channels expecting one element put onto the channel at a time

okwori18:06:27

If I could get it working in one of the functions using coll, I will be just fine. Those functions were written for different purposes earlier and am just trying to see how to make it faster

eriktjacobsen18:06:45

I get that, i'm just pointing out that functions passing around things at the collection level are going to be harder to use with core.async than ones taking an element at a time, and result in a more confusing flow, imho.

okwori18:06:04

@eriktjacobsen Okay, I could work around that, but the first fn for example does the single API call

eriktjacobsen18:06:25

gotcha, sec writing up example

swizzard18:06:27

is there anything like -> except the value is threaded in as the first thing in every form?

swizzard18:06:44

i guess i could use as-> but would rather not?

okwori18:06:02

@swizzard Good point, though here it is been used as the first thing in the forms. I had to remind myself of the flexibility as-> provides

swizzard18:06:33

i feel like i lean on as-> a little too much

timgilbert18:06:20

You could check out https://github.com/rplevy/swiss-arrows, there are a bunch more variations although it's all a bit punctuation-heavy for my taste

swizzard18:06:13

yeah, i’ve looked into that before

swizzard18:06:19

seems a bit overkill for just this one thing

victora18:06:50

How can I integrate unit tests to my compojure app?

donyorm18:06:09

I think you need to look into the clojure.test library

victora18:06:31

timgilbert: I'll def. check that out

timgilbert18:06:09

Cool. We use it a lot for "user does this, then user does this, then user does this..." type of tests

timgilbert18:06:48

Not the prettiest API but you can use it to build pretty decent integration tests

timgilbert18:06:40

("integration" in that they test your app's ring integration, I guess)

victora19:06:34

I need that java method that goes back in time to a fixed point, for tests. Can anyone give me the clojure alternative/way?

seancorfield20:06:05

@victora I don’t know what you’re using for writing your tests but that’s something Expectations supports… If I’m understanding your question.

victora20:06:49

I'm using midje and compojure

seancorfield21:06:51

Hmm, bit surprised Midje doesn’t have a freeze time option. It seems to have all sorts of weird and fancy feature… Must admit, I don’t like its non-Clojurey DSL so I’ve never really dug into it.

timgilbert20:06:17

Not sure which method you mean?

victora20:06:33

timgilbert: I can't seem to find the name, but it was a "go to following date and execute this function" method

eriktjacobsen20:06:42

@simon had to step out for lunch. I'm not really a big core.async user, so I'm sure better ways to do this. I think reducers or simply pmap would work well here, since you want to collect all the results before the last step, instead of processing each unit of data by itself.

noisesmith21:06:15

about the original process-points, it’s never neccessary to use _ to bind items at the end of a let block, you can just put them in the function body

noisesmith21:06:23

defn has an implicit do block

noisesmith22:06:54

also '() is just () and as an arg to into it leads to reversing your input

noisesmith22:06:22

(that might be intentional?)

okwori22:06:28

Thanks guys @eriktjacobsen and @noisesmith. I am trying to decipher and implement it now. Given am in Europe, caffeine is barely keeping my eyes open now but in any case I should have some feedback soonest

eriktjacobsen22:06:06

Also, the take is redundant as to-chan closes the channel after exhausting input coll. I found that out after, but left it in for clarity / example.