Fork me on GitHub
#beginners
<
2022-09-28
>
Chris12:09:37

Hi! I’m extracting some keys from a json file and get back a vector of maps

({:name "name1", :id "61fd12debd75d9413d874a42"}
 {:name "name2", :id "61dd3720ac8ceb2d1f4f8419"}
 {:name "name3", :id "61dd404da88a335e39ac984b"})
Now I’d like to create something like a combined lookup table, so something like
"61fd12debd75d9413d874a42" name1
"61dd3720ac8ceb2d1f4f8419" name2
...
so I can get the id and have the name returned. But I’m somehow failing to come up with the functions needed to do that, could somebody point me in the right direction?

J12:09:12

Hi @U043ZD1K5TL . There are many ways to do that but you can take a look the reduce function (https://clojuredocs.org/clojure.core/reduce)

Martin Půda13:09:00

(->> '({:name "name1", :id "61fd12debd75d9413d874a42"}
       {:name "name2", :id "61dd3720ac8ceb2d1f4f8419"}
       {:name "name3", :id "61dd404da88a335e39ac984b"})
     (mapv (juxt :id :name))
     (into {}))

❤️ 1
Chris13:09:39

thanks, that works perfectly! I’ll play around with reduceand juxt

Martin Půda13:09:41

Or this one:

(->> '({:name "name1", :id "61fd12debd75d9413d874a42"}
       {:name "name2", :id "61dd3720ac8ceb2d1f4f8419"}
       {:name "name3", :id "61dd404da88a335e39ac984b"})
     (reduce (fn [m {:keys [id name]}]
               (assoc m id name)) {}))

Chris13:09:37

ah cool, thank you

Chris13:09:00

@U01RL1YV4P7 is it possible to use the #() form for the anonymous function or is fnneeded?

Martin Půda13:09:46

You can do that:

(->> '({:name "name1", :id "61fd12debd75d9413d874a42"}
       {:name "name2", :id "61dd3720ac8ceb2d1f4f8419"}
       {:name "name3", :id "61dd404da88a335e39ac984b"})
     (reduce #(assoc %1 (:id %2) (:name %2)) 
             {}))

Chris13:09:45

ok, so if I want to do destructuring it has to be fn I guess

Martin Půda13:09:22

Well, you can also destructure in let...

(->> '({:name "name1", :id "61fd12debd75d9413d874a42"}
       {:name "name2", :id "61dd3720ac8ceb2d1f4f8419"}
       {:name "name3", :id "61dd404da88a335e39ac984b"})
     (reduce #(let [{:keys [id name]} %2]
                (assoc %1 id name)) 
             {}))

Chris13:09:58

I see, thanks!

quoll14:09:23

Since juxt is often an answer looking for a problem, I feel like I should mention it whenever it’s applicable! 🙂

(->> your-data
     (map (juxt :id :name))
     (into {}))

quoll14:09:39

I like this approach, because it emphasizes composability of core functions, and it’s efficient

1
Jim Strieter15:09:11

I'm trying to use this function (https://clojuredocs.org/clojure.edn/read#example-5a68f384e4b09621d9f53a79) to load an .edn file. The result should be a nested map. Here is the function:

(defn load-edn
  "Load edn from an io/reader source (filename or io/resource)."
  [source]
  (try
    (with-open [r (io/reader source)]
      (edn/read (java.io.PushbackReader. r)))

    (catch java.io.IOException e
      (printf "Couldn't open '%s': %s\n" source (.getMessage e)))
    (catch RuntimeException e
      (printf "Error parsing edn file '%s': %s\n" source (.getMessage e)))))
When I run:
my-project=> (load-edn "filename.edn")
I expect this:
{:a 42 :b {:c 99 :d 101}}
Instead I get this:
Error parsing edn file 'filename.edn': No reader function for tag object
The code is copied from the document. So is there something outdated in the documentation? Or do I need to create a reader function?

dpsutton15:09:37

can you paste the contents of the edn file?

dpsutton15:09:52

I suspect you serialized something in there that cannot be read

Jim Strieter15:09:06

It's over 1 MB so I can't post all of it

Jim Strieter15:09:40

Let me see what I can post....

Jim Strieter15:09:03

{:acct {:init-cash 10000.0, :option-book 155181.2234612884, :cash 10000.0, :total 10000.0, :drawdown 0, :drawdown-percent 0, :last-settled-value 10000.0, :all-time-high 10000.0}, :state-logfile-directory "cached-state/", :potential-returns (), :stop-epoch 1672549200, :orders {}, :late-in-day true, :paid-this-month-already false, :enable-punishment false, :last-bar-in-day-tol 450, :epoch 1664369100, :rec ord-spot-ratio true, :strike-fraction-vec [1.0], :punishment-fn #object[clojure_trade_bot_2.punishment_functions$no_punishment 0x3753db6e "clojure_trade_bot_2.punishment_functions$no_punishment@3753db6e" ], :amount-paid-this-month 0, :log-all-triggers false, :datasource #object[next.jdbc.connection$url_PLUS_etc$reify__1995 0x2ee99820 "jdbc:<postgresql://192.168.56.1:5432/mockstockdata>"], :prev-late-in-day true, :trades-only "trades-only.csv", :early-in-day-tol 4050.0, :update-sigma #object[clojure_trade_bot_2.loop_state$fn__12571 0x5dfff935 "clojure_trade_bot_2.loop_state$fn__12571@5dfff935"], :tickers-u pdated {}, :state-logfile-format-str "cached-state/indicator-states-%s.edn", :histogram {:60 1.1, :70 1.2, :80 1.7, :90 2.5, :95 3.5, :99 5}, :pay-thresh 200000, :pickle-trigger-epoch 1663764872, :prev-p ositions {:AHC {:ticker :AHC, :entry-price 18.369068423954616, :epoch 1642691700, :qty 7.0, :direction :up, :ratio 1.0, :strike 645.0, :entry-spot 644.88}, :NEU {:ticker :NEU, :entry-price 8.498051910590 874, :epoch 1642775400, :qty 17.0, :direction :up, :ratio 1.0, :strike 342.5, :entry-spot 338.53}, :DAC {:ticker :DAC, :entry-price 1.8082271790284636, :epoch 1656603904, :qty 80.0, :direction :up, :rati o 1.0, :strike 91.5, :entry-spot 89.86}, :CRC {:ticker :CRC, :entry-price 3.1913637679909854, :epoch 1642691700, :qty 45.0, :direction :up, :ratio 1.0, :strike 113.5, :entry-spot 113.4}, :XRM {:ticker :X RM, :entry-price 4.82937735211199, :epoch 1642691700, :qty 30.0, :direction :up, :ratio 1.0, :strike 169.0, :entry-spot 169.0}, :CNS {:ticker :CNS, :entry-price 2.660726741128059, :epoch 1642775400, :qty 55.0, :direction :up, :ratio 1.0, :strike 100.5, :entry-spot 100.09}, :ROG {:ticker :ROG, :entry-price 6.8424128683807055, :epoch 1642775400, :qty 21.0, :direction :up, :ratio 1.0, :strike 272.5, :entry -spot 270.24}, :SAM {:ticker :SAM, :entry-price 13.076930201045968, :epoch 1642775404, :qty 11.0, :direction :up, :ratio 1.0, :strike 507.5, :entry-spot 476.41}, :OXM {:ticker :OXM, :entry-price 2.596691 3186364664, :epoch 1642775400, :qty 56.0, :direction :up, :ratio 1.0, :strike 102.5, :entry-spot 101.85}, :FIX {:ticker :FIX, :entry-price 2.1879612022181405, :epoch 1642775400, :qty 66.0, :direction :up , :ratio 1.0, :strike 101.0, :entry-spot 99.18}}, :prev-theoretical-profits {}, :early-termination-thresh 1.0E7, :late-in-day-tol 4050.0, :enable-trading true, :first-bar-of-day false, :sigma {}, :prev-d ay-of-week "nada", :sell-before-end-of-day true, :set-order-limits #object[clojure_trade_bot_2.loop_state$fn__12573 0x63f3fef1 "clojure_trade_bot_2.loop_state$fn__12573@63f3fef1"], :last-bar-in-day false , :update-position-performance #object[clojure_trade_bot_2.loop_state$fn__12575 0x4a52d14a "clojure_trade_bot_2.loop_state$fn__12575@4a52d14a"], :this-day-num 27, :rand-str "igy7rt3k6nc41b5a", :update-st rikes #object[clojure_trade_bot_2.loop_state$fn__12577 0x281868b8 "clojure_trade_bot_2.loop_state$fn__12577@281868b8"], :start-trading-epoch 1641048304, :prev-epoch 1609512304, :log-all-ratios true, :kee p-going-historical true, :update-account-value #object[clojure_trade_bot_2.loop_state$fn__12579 0xa0c0adf "clojure_trade_bot_2.loop_state$fn__12579@a0c0adf"], :update-orders #object[clojure_trade_bot_2.l oop_state$fn__12581 0x76f47919 "clojure_trade_bot_2.loop_state$fn__12581@76f47919"], :profits (), :current-theoretical-profits {}, :prev-early-in-day false, :physical-epoch 1664369669, :first-bar-in-day- tol 450, :sell-threshold 5, :update-positions #object[clojure_trade_bot_2.loop_state$fn__12583 0x2af03e0c "clojure_trade_bot_2.loop_state$fn__12583@2af03e0c"], :max-spread 20, :enable-early-termination t rue, :tickers-to-display (:EAF :OMN :NOMD :NKX :MSGN :CVI :NUW :COF :DXR :GWB :JEQ :HRC :CLR :ETX :QEP :ELJ :JBR :BKHU :UFS :BGB :EEA :FCN :GJR :NLSN :VCF :SKM :WNC :MT :SALT :RZB :BK :CHE :BCX :JELD :EQ T :SNDR :MUA :FFG :SITE :EGY :GLOG :HVT :PFSI :AZN :SCI :NDP :HCI :PLOW :EVR :VKQ :EFC :AWP :UNVR :HUD :OAK :RHE :JNPR :GBL :ZBH :CMA :FFA :MAS :MYJ :GSBD :FSM :SLCA :CMCM :AER :MHI :BKK :DSE :BST :MSN : FUL :AIZP :EFF :MXC :XIN :BURL :VSH :HOV :EXP :MTW :CSLT :CPF :CIVI :STG :SRCI :HNP :EVY :SAFE :BGCA :NCA :BCE :MHH :GOLF :UZA :XRX :LOR), :model-order-fill #object[clojure_trade_bot_2.loop_state$fn__125 85 0x4146ce42 "clojure_trade_bot_2.loop_state$fn__12585@4146ce42"], :indicator-fns {:indicator-1 #object[clojure_trade_bot_2.loop_state$fn__12587 0x2b767e47 "clojure_trade_bot_2.loop_state$fn__12587@2b76 7e47"], :indicator-2 #object[clojure_trade_bot_2.loop_state$fn__12589 0x7e1fe681 "clojure_trade_bot_2.loop_state$fn__12589@7e1fe681"]}, :prev-first-bar-of-day false, :prev-indicator-states {:PLOW {:iir-s tates {:iir-slow {:weights [{:b0 2.833901381447552E-6, :b1 5.667802762895104E-6, :b2 2.833901381447552E-6, :a1 -1.9966336134561717, :a2 0.9966477771275631} {:b0 4.41624156086296E-6, :b1 8.83248312172592E -6, :b2 4.41624156086296E-6, :a1 -1.9929859170614943, :a2 0.993000054856945} {:b0 0.0018797747856513227, :b1 0.0018797747856513227, :b2 0.0, :a1 -0.9962404504286974, :a2 0.0} {:b0 3.5370172737914096E-6, :b1 7.074034547582819E-6, :b2 3.5370172737914096E-6, :a1 -1.994434178813129, :a2 0.9944483268822241} {:b0 2.183838619369596E-6, :b1 4.367677238739192E-6, :b2 2.183838619369596E-6, :a1 -1.9992909798874585 , :a2 0.9993051624096118} {:b0 3.949329870503769E-6, :b1 7.898659741007538E-6, :b2 3.949329870503769E-6, :a1 -1.995456338892737, :a2 0.9954704942128065} {:b0 5.141012016325428E-6, :b1 1.0282024032650857E -5, :b2 5.141012016325428E-6, :a1 -1.9926082148627287, :a2 0.9926223499788447} {:b0 3.535540421983896E-6, :b1 7.071080843967792E-6, :b2 3.535540421983896E-6, :a1 -1.993601419630258, :a2 0.993615561791946 1} {:b0 3.543210501829165E-6, :b1 7.08642100365833E-6, :b2 3.543210501829165E-6, :a1 -1.9979263828617726, :a2 0.9979405557037805}], :states [{:sample0 27.85, :sample1 27.98, :sample2 27.93, :state0 24.20 886529629913, :state1 24.214437224859612, :state2 24.220001500922162} {:sample0 24.20886529629913, :sample1 24.214437224859612, :sample2 24.220001500922162, :state0 32.01398853180653, :state1 32.01595591 490967, :state2 32.01791210337123} {:sample0 32.01398853180653, :sample1 32.01595591490967, :sample2 32.01791210337123, :state0 31.95795727148643, :state1 31.957742112045977, :state2 31.957518737386525} {:sample0 31.95795727148643, :sample1 31.957742112045977, :sample2 31.957518737386525, :state0 31.363908659704876, :state1 31.36270930484591, :state2 31.361511719923143} {:sample0 31.363908659704876, :sa mple1 31.36270930484591, :sample2 31.361511719923143, :state0 19.662294604510556, :state1 19.664421204976005, :state2 19.666544353982843} {:sample0 19.662294604510556, :sample1 19.664421204976005, :sampl e2 19.666544353982843, :state0 21.67835902272997, :state1 21.676104840872984, :state2 21.673844232506006} {:sample0 21.67835902272997, :sample1 21.676104840872984, :sample2 21.673844232506006, :state0 31 .962869959673192, :state1 31.965876686625826, :state2 31.968899622793604} {:sample0 31.962869959673192, :sample1 31.965876686625826, :sample2 31.968899622793604, :state0 35.25621372830657, :state1 35.264 22489970006, :state2 35.27224060112758} {:sample0 35.25621372830657, :sample1 35.26422489970006, :sample2 35.27224060112758, :state0 39.175415424078636, :state1 39.18666501908188, :state2 39.197882122977 69}], :scale-vals [1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0000000001424068]}

dpsutton15:09:28

`:punishment-fn #object[clojure_trade_bot_2.punishment_functions$no_punishment 0x3753db6e “clojure_trade_bot_2.punishment_functions$no_punishment@3753db6e” ],`

dpsutton15:09:30

:punishment-fn #object[clojure_trade_bot_2.punishment_functions$no_punishment 0x3753db6e "clojure_trade_bot_2.punishment_functions$no_punishment@3753db6e"
],

dpsutton15:09:36

you serialized a function

dpsutton15:09:48

(try
  (let [f (java.io.File/createTempFile "test" "edn")]
    (spit f (pr-str {:a 1 :b inc}))
    (println "file contents:\n" (slurp f))
    (clojure.edn/read-string (slurp f))))
file contents:
 {:a 1, :b #object[clojure.core$inc 0xacbdcab "clojure.core$inc@acbdcab"]}
Execution error at metabase.api.dashboard-test/eval152920 (REPL:74).
No reader function for tag object

Jim Strieter15:09:15

Lemme take that out...

Jim Strieter15:09:36

@U11BV7MTK I took out serialized functions, and now I'm getting this error: Error parsing edn file 'cached-state/indicator-states-2022-09-27.edn': No reader function for tag object

Jim Strieter15:09:41

What does that mean?

dpsutton15:09:40

its the same error right?

quoll15:09:03

It means that it found a sequence that begins with a # character (like #object[...] )

dpsutton15:09:08

how would you read this value? #object[clojure.core$inc 0xacbdcab "clojure.core$inc@acbdcab"]

quoll15:09:41

Serializing native objects and functions will do this.

quoll15:09:42

I think you need to scrub the file, looking for anything starting with #

Jim Strieter15:09:20

Ahhhhhhhhhhhhh

quoll15:09:51

Some things can be serialized safely, like #uuid but that’s not all that common in edn

Jim Strieter15:09:31

You can serialize a hash set right? #{3 4 5 :fred}

dpsutton15:09:52

dashboard-test=> (try
  (let [f (java.io.File/createTempFile "test" "edn")]
    (spit f (pr-str #{1 2 :fred}))
    (println "file contents:\n" (slurp f))
    (println "read contents:\n" (clojure.edn/read-string (slurp f)))))
file contents:
 #{:fred 1 2}
read contents:
 #{:fred 1 2}
nil

dpsutton15:09:55

play at the repl

quoll15:09:38

You can serialize sets. Clojure does not care if it’s a hashset

dpsutton15:09:39

but only if you can serialize everything in the set

(let [f (java.io.File/createTempFile "test" "edn")]
  (spit f (pr-str #{1 2 inc}))
  (println "file contents:\n" (slurp f))
  (println "read contents:\n" (clojure.edn/read-string (slurp f))))
file contents:
 #{1 #object[clojure.core$inc 0xacbdcab "clojure.core$inc@acbdcab"] 2}
Execution error at metabase.api.dashboard-test/eval152930 (REPL:102).
No reader function for tag object

Jim Strieter02:09:39

Thanks everybody! What wound up working was selecting only the keys from the map that were really necessary. After I did that, all the mysterious #'s went away

Ray Barnett18:09:31

What is the best way to secure your first job with Clojure? I have noticed most of the open positions require about 3 years of prior experience.

emccue18:09:26

first job with clojure or first job, that job is writing clojure?

seancorfield18:09:36

#jobs-discuss is the best channel for this kind of discussion (and you'll see several similar discussions in the past if you scroll back there).

ahungry19:09:39

I didn't think anywhere hired for lisps - you have to infiltrate under the guise of a language like nodejs/php/ruby/python and get them to adopt it from the inside out

👍 1
Ray Barnett19:09:18

First job with Clojure.

seancorfield19:09:39

@U96DD8U80 There are plenty of Clojure shops out there -- in some parts of the world. So location is important.

seancorfield19:09:22

I mean, not anywhere near as many as Scala or Kotlin shops, but still there are definitely places hiring for Clojure roles.

seancorfield19:09:55

@U043D8YDWQZ If you scroll back in #jobs-discuss you'll see quite a bit of advice there.

👍 1
Ray Barnett21:09:47

Thank you everybody.

rschmukler19:09:08

Is there any way to get the form that was used when creating a clojure var / top level definition in a namespace? Currently I am using tools.reader to look at a file but that has the disadvantage of potentially going stale as a user interacts with the code in the repl but doesn’t save the code to the file.

Alex Miller (Clojure team)19:09:51

from the most general perspective, no. function objects are compiled and do not contain their source

👍 1
Alex Miller (Clojure team)19:09:45

unless you are doing something extra, the things you eval in your repl do not get saved anywhere (but they could be if you hack your repl!)

rschmukler19:09:09

So then, the general wisdom would be to write a macro def that potentially stores the form etc on var metadata…

Alex Miller (Clojure team)19:09:39

I think it would be better to make this the job of your evaluator than embed it in your program

emccue19:09:19

(defmacro definitely
  [symbol form]
  `(def ~(with-meta symbol 
                    (merge (meta symbol) {:form `(quote ~form)}))
        ~form))

rschmukler19:09:53

The concern there is that from a library perspective that it’s potentially asking users more to configure a custom nrepl middleware or something similar. Aren’t you then also potentially pushing the complexity of macroexpansion onto your evaluator?

Alex Miller (Clojure team)19:09:47

doesn't have anything to do with expansion

rschmukler19:09:31

What I mean is, consider a user (programmer) defined macro

(defmacro define-name [x]
  (def name-~x ~(str x))

(define-name bob)
The evaluator must now understand that (eval '(define-name bob)) will expand, no?

rschmukler19:09:09

(I apologize if I am confusing terminologies here)

emccue19:09:42

(fixed the example)

Alex Miller (Clojure team)19:09:50

if you get into wrappers around core var creators, then yes, you might need something else

emccue19:09:06

user=> (defmacro definitely
  [symbol form]
  `(def ~(with-meta symbol
                    (merge (meta symbol) {:form `(quote ~form)}))
        ~form))
#'user/definitely
user=> (definitely x (+ 1 2))
#'user/x
user=> (meta #'x)
{:form (+ 1 2), :line 1, :column 1, :file "NO_SOURCE_PATH", :name x, :ns #object[clojure.lang.Namespace 0x312afbc7 "user"]}
user=>

emccue19:09:54

I think morphe/defn might be able to support this as an aspect?

emccue19:09:26

again - if you are getting into wrappers around core var creators

rschmukler19:09:54

Yeah, I think I will have to. My next question (and please don’t throw tomatoes): Is there any way to hot-patch def so that I, as an end user, don’t need to use a special my-ns/def call?

emccue19:09:31

Fork clojure

rschmukler19:09:13

Haha damn. No way to do it on a live repl? I realize we are essentially editing a special form, so I am not surprised that this might be the case

emccue19:09:16

user=> (defmacro def [a] `(+ ~a ~a))
#'user/def
user=> (def 4)
Syntax error compiling def at (REPL:1:1).
First argument to def must be a Symbol

emccue19:09:58

lets take a step back maybe

emccue19:09:04

what are you trying to do?

emccue19:09:41

because it sounds like you are running into the same issue #core-typed does with the potentially stale file

emccue19:09:51

and afaik there isn't a great resolution there

rschmukler19:09:01

So, I have https://github.com/teknql/systemic that I use to make a dependency graph of systems. This makes it so that starting a system will automatically start its sub-systems..

(defsys *env*
  (System/getEnvironment "ENV"))

(defsys *db-url*
  (case *env*
    "prod" prod-url
    "dev" dev-url))
  
When using the dependent system directly in the macro it is easy enough to establish the dependency hierarchy. But sometimes a system calls a function that depends on a system, and then that requires specifying manual dependencies. In practice I’ve found this to be quite fragile and so I would like to automatically traverse the call tree and establish dependencies…
(defsys *env*
  (System/getEnvironment "ENV"))

(defn get-db-url
  []
  (case *env*
    "prod" prod-url
    "dev" dev-url))
  

(defsys *db-url*
  (get-db-url))

rschmukler19:09:20

Apologies for the silly example. Obviously you’d never really write code like this, but I hope it sort of showcases the issue.

emccue19:09:28

Mount does this

emccue19:09:31

(not saying stop writing, but there is some prior work to look at)

emccue19:09:49

ahh nvm you are using that library

emccue19:09:03

you are touching upon one of the core conflicts of the ecosystem

rschmukler20:09:03

Yeah, I am familiar with mount. Systemic was heavily inspired by it, with the core difference being that I wanted the ability to be able to run multiple isolated systems in the same repl (to allow for testing and deving in the same repl session).

rschmukler20:09:59

But it looks like there’s no great answer. Morphe looks like an interesting avenue. Perhaps I’ll look into a per-application defn that is built on-top of of morphe.

ahungry19:09:09

Will a pmap'ed evaluation retain the same order of elements that a map'ed one would? (like, (pmap inc (range 10)) - but lets assume 'inc' is a function with a lot of stuff going on under the hood, where evaluation time may be different between calls)

rschmukler19:09:52

Yes, it will maintain order

(pmap
  (fn [x]
    (Thread/sleep (long (rand-int 1000)))
    (inc x))
  (range 10))

;; => (1 2 3 4 5 6 7 8 9 10)

thanks3 1
Ben Lieberman19:09:00

I didn't mean to delete that only edit it so here goes once more: is there different syntax for reading tagged maps (i.e. not (:key-name map))

seancorfield19:09:22

What do you mean by "tagged maps"?

seancorfield19:09:00

#:foo{:bar 42} is shorthand for {:foo/bar 42} so you'd access that with (:foo/bar data)

🙏 1
Ben Lieberman19:09:10

like this #:tick{:beginning #time/time "06:19", :end #time/time "21:42"}

Ben Lieberman19:09:19

so I guess what you said applies here

seancorfield19:09:17

user=> (println #:tick{:beginning "06:19", :end "21:42"})
#:tick{:beginning 06:19, :end 21:42}
nil
user=> (binding [*print-namespace-maps* false] (println #:tick{:beginning "06:19", :end "21:42"}))
{:tick/beginning 06:19, :tick/end 21:42}
nil
user=>

Ben Lieberman19:09:54

Cool, didn't know about binding either. Thanks!

Chris20:09:31

I have a function

(map (juxt :description calculate-duration-dec) day-sorted-by-project)
that returns
(["cluster debugging" 4.0] ["daily" 0.25])
and I’m trying to combine the first and second elements of all lists so I will get
cluster debugging, daily" 4.25
I tried around a bit with mapcat, but I’m not really getting back what I want

seancorfield20:09:50

They're all the same format [string number] and you want a single pair at the end that is [string number]?

1
seancorfield20:09:47

There are a couple of ways you could do this, depending on how long the list of data is and how efficient you need it to be.

seancorfield20:09:45

If you don't care much about either, then [(str/join ", " (map first data)) (apply + (map second data))] is probably going to be reasonable.

seancorfield20:09:21

If you want something more efficient, you probably want to reduce over day-sorted-by-project and produce a pair whose first element is a vector of the :description values (that you can then str/join) and whose second element is the sum.

seancorfield20:09:30

Something like...

(let [[descriptions sum] 
      (reduce (fn [[ds s] day] 
                [(conj ds (:description day)) 
                 (+ s (calculate-duration-dec day))]) 
              [[] 0.0] 
              day-sorted-by-project)] 
  [(str/join ", " descriptions) sum])

Chris20:09:26

thanks very much, I’ll test it out and have a closer look tomorrow. I kept thinking about needing two reduces, seems like it always turns out more complicated in my head

seancorfield20:09:02

You could probably do the whole thing with transduce since your reducing function could have a completing arity, but I'd need to play in the REPL for that 🙂

Chris20:09:44

that would probably go over my head as I haven’t touched transducers yet 😄

Michaël Salihi20:09:44

Hi, is there a better way to check if all selected keys's values in each maps are equal to zero?

(def x [{:a 0
         :b 0
         :c 1}
        {:a 0
         :b 0}])

(->> x
     (map #(select-keys %[:a :b]))
     (map vals)
     flatten
     (every? zero?))
#_=> true

rolt21:09:55

really tiny improvment: (map vals) flatten can be replaced by (mapcat vals)

👍 1
Alex Miller (Clojure team)21:09:56

(zero? (reduce + (mapcat (juxt :a :b) x))) ?

👌 1
Alex Miller (Clojure team)21:09:09

(I guess assuming no negatives :)

rolt21:09:10

also: `(mapcat (juxt :a :b) x) shoud work

👍 1
rolt21:09:17

oups too late

🙂 1
Alex Miller (Clojure team)21:09:36

I guess every? zero? probably better

pppaul22:09:20

(every? zero? (mapcat (juxt :a :b) x))

💯 1
pppaul22:09:20

oh, i missed the first juxt solution 🙂

Michaël Salihi08:09:56

Thank you all for your advice and solutions. Very useful! 👍

JohnJ04:09:39

x.flatMap(o => [o.a, o.b]).every(i => i === 0) come to the dark side, it's smoother here

quoll13:09:53

This is almost exactly the same as the last solution! I like juxt more, but if I were trying to do the precise equivalent, then I’d say: (every? zero? (mapcat (fn [{:keys [a b]}] [a b]) x))

👍 1
ric21:09:05

Found a weird behavior when shadowing a clojure.core fn.

(ns ns1)
(defn print-str [] 111)

(ns ns2)
(require '[ns1])
(defn x [] (ns1/print-str))
(x) ;; => 111, everything looks fine

;; now if I change the print-str fn
(ns ns1)
(defn print-str [] 999)

;; and reexecute (x), I expect it to return the updated value 999
(ns ns2)
(x) ;; => but no, still returns the old value 111
Is my expectation wrong? If the fn has any other name that doesn't shadow a core fn, it works as expected. e.g. something-else instead of print-str

Jon Boone21:09:28

When you evaluate the form (defn x [] (ns1/print-str)) would you not expect the invocation of x subsequently to use the binding of ns1/print-str that existed at that time of the binding of x as a function?

Jon Boone21:09:41

Also what fn are you referring to when you say: If the fn has another name, it works as expected

ric22:09:02

Yep, I expect ns1/print-str to be looked up at the time of execution. Sorry, that was badly worded. If I use another fn name instead of print-str , e.g. something-else , it works as expected.

dpsutton22:09:34

This looks like a legit bug to me

hiredman23:09:44

Looks like there is some work in 1.12 related to this https://clojure.atlassian.net/browse/CLJ-2712

thanks2 1
hiredman23:09:17

That ticket has some links to other tickets that talk about the issues that arise when you shadow vars like that without excluding them first. vars coming from clojure.core are just the most common place you'll run into it because by default ns makes all of clojure.core available