Fork me on GitHub
#beginners
<
2019-02-20
>
Piotr06:02:26

Hi, looking for some simple solution: I have :

[["John" 32 12.3] ["Kate" 66 32.4]...]
Want to cast it to vector of maps:
[{:name "John" :age 32 :some-val 12.3} ....
I have come up with:
(mapv (fn [x]
            (let [[a b c] x]
            {:name a :age b :value c})) 
  [["John" 32 1.2]["Kate" 45 12.2]])
Any better ideas?

Chris08:02:03

I like for macros for this job:

(for [[n a v] data]
  {:name n :age a :value v})

Ô׼ 5
­čĹŹ 5
seancorfield06:02:40

@piotr.kurnik zipmap would probably help you here...

seancorfield06:02:11

(mapv (partial zipmap [:name :age :value]) list-of-vecs)

Piotr19:02:02

For some reason I really like this particular solution!

practicalli-john09:02:33

What are your preferred ways of consuming API's, either sever or client (ClojureScript) side. I'm starting off with a simple JSON API, but will be consuming more interesting ones soon. Do you just use slurp or http-kit for simple Api's? Liberator looks interesting for server side, any thoughts? Are you using Transit to manage transformation of formats? Or do you just drop all the data as a nice Clojure map onto a Kafka topic? Interested in understanding what people are using and why. Thanks.

ordnungswidrig09:02:15

liberator is for providing http apis, not for consuming.

curlyfry15:02:55

I've been using https://github.com/JulianBirch/cljs-ajax client side and https://github.com/http-kit/http-kit server side. I use transit if I am the only provider/consumer of the API, regular JSON otherwise.

­čĹŹ 5
jumar08:02:00

speaking of server side: It depends - for simpler use cases we tend to use clj-http directly. In some cases we use wrappers such as https://github.com/Raynes/tentacles - we use github api quite extensively and this saves some effort; however, on one occasion, we've also used clj-http to call graphql api (because of performance reasons)

­čĹŹ 5
practicalli-john20:02:21

Thanks everyone, most helpful.

Audrius18:02:57

why this

(defn ok [y] 5)

(s/fdef ok
        :args string?
        :ret true?
        :fn true?)
is not enforced?

Lennart Buit18:02:39

args needs to be (s/cat :y string?)

Audrius18:02:48

how to call it to get error for not conforming?

Audrius18:02:17

I fixed as you say, but I can run on any input...

Lennart Buit18:02:27

Right, you need to instrument

Lennart Buit18:02:36

Specs are not checked by default

Audrius18:02:01

and how to do?

Lennart Buit18:02:46

So you can call instrument from clojure.spec.test.alpha, but that will only check :args specs. So it will detect a spec failure when you call (ok 5)

Lennart Buit18:02:26

If you also want your :ret and :fn specs to be checked, you can use something like orchestra: https://github.com/jeaye/orchestra

Audrius18:02:11

ok I can see

Chase19:02:23

I was trying to recreate the square root function from SICP: http://sarabander.github.io/sicp/html/1_002e1.xhtml#g_t1_002e1_002e7 in clojure. I came up with this but my answer keeps coming out wrong:

(defn average [x y]
  (/ (+ x y) 2))

(defn improve [guess x]
  (average guess (/ x guess)))

(defn abs [x]
  (if (< x 0)
    (- x)
    x))

(defn square [x]
  (* x x))

(defn good-enough? [guess x]
  (< (abs (- (square guess) x)) 0.001))

(defn try [guess x]
  (if (good-enough? guess x)
    guess
    (recur (improve guess x) x)))

(defn sqrt [x]
  (try 1.0 x))

(sqrt 2.0)
;; => 2.0
I went to some "SICP in Clojure" resources and still can't seem to find what I'm doing wrong. Anyone notice what's funky?

hiredman19:02:24

you can't call a function named try like

hiredman19:02:29

try is a special form

hiredman19:02:21

so (try 1.0 x) isn't call your function try, it is compiling as the try/catch special form without a catch, so (try 1.0 x) ~= (do 1.0 x)

Chase19:02:39

oh how funny. I should have caught something like that. thank you!

alex mcknight21:02:22

A nice way to start learning clojure web apps https://github.com/mcknight816/cjweb

Chase21:02:43

Nice! I'll check this out. I've been starting to slowly dip my toes into web dev. Would you consider Mongo DB beginner friendly? Is that interchangeable?

alex mcknight22:02:11

I think it is. Future improvements might be to allow other no-sql databases. The code minus the comments is less then 100 lines. I'm new to clojure and this just represents what Iv'e learned so far. Truly a powerful language.

Chase22:02:38

Sounds good to me. I appreciate you putting it out there for others to learn from

chocksmith23:02:37

(defn process-node [node] (def next (first (:event-list node))) (if (nil? next) "DONE" (do (println next) ;; do something with next ;; and return the node without the first event of the event-list (assoc node :event-list (pop (:event-list node)))))) On the code above. 1) Should I use 'let' instead of 'def'? 2) Is there any better idiomatic way of doing it?

noisesmith23:02:23

def creates a new var at namespace scope

noisesmith23:02:31

it's pretty much never what you want inside defn

­čĹŹ 5
noisesmith23:02:27

so yes, the normal thing would be to use let (also the name next clashes with a function in clojure.core, so it might be good to use a different name as well)

noisesmith23:02:45

also, checking for first returning nil means your code can't handle an input like {:event-list [:a nil :b]} - totally fine if nil would never make sense, but do be aware of the limit of the pattern. The reliable thing is to check if (seq (:event-list node)) is nil.

­čĹŹ 5
noisesmith23:02:40

also, that pattern of iteration could be replaced with (doseq [event (:event-list node)] (frob event)) - no need to manage list traversal by hand

noisesmith23:02:56

or even (run! frob (:event-list node))

noisesmith23:02:46

oh - you aren't iterating here though

chocksmith23:02:50

and what happens if I need to include new events on the list after processing 'next'? Sometimes one event creates two or more others. In this case I cannot use doseq. Can I?

noisesmith23:02:09

no, it sounds like you aren't just doing a simple iteration here

chocksmith23:02:43

you got it. It is part of a much more complex logic.

noisesmith23:02:01

this would still fit a loop/recur pattern though

chocksmith23:02:24

sometimes it will return an (assoc ....) that will include new elements on the list. S├│ I have to manually transverse the list...

chocksmith23:02:34

yes. using loop!

chocksmith23:02:24

When I wrote (assoc node :event-list (pop (:event-list node))) I wondered if exists a better idiomatic way of doing it. Any kind of "pop-in"... ­čśë

noisesmith23:02:43

(update node :event-list pop) or (update-in node [:event-list] pop)

hiredman23:02:52

[:event-list]

noisesmith23:02:32

but consider using rest instead of pop, because if a vector will pop in a different order

chocksmith23:02:13

No because I am using https://github.com/clojure/data.priority-map . But you are right! rest is better!

hiredman23:02:40

if you are using pop you should use peek instead of first

noisesmith23:02:41

(and you can't pop a set at all, etc.)

chocksmith23:02:49

Beautiful! There is always a better way of doing it in Clojure. I love it!

hiredman23:02:51

pop:peek::rest:first

­čĹŹ 5
noisesmith23:02:03

right, definitely stick to one pair and not mix them

noisesmith23:02:50

(related, rest on a clojure.lang.PersistentQueue returns a seq, which turns your FIFO into a LIFO)

­čĹŹ 5
noisesmith23:02:09

rest will have the same problem as a queue here: calling rest on a priority-map probably won't return a priority-map, it's most likely to return a list, which is simple LIFO

noisesmith23:02:48

because rest calls its seq method, and the seq method returns a list not a priority-map

chocksmith23:02:33

tmp=> (type (rest (pmap/priority-map :a :b))) clojure.lang.LazySeq tmp=> (type (pop (pmap/priority-map :a :b))) clojure.data.priority_map.PersistentPriorityMap

noisesmith23:02:49

right - you want pop as it preserves the prioritizing feature

chocksmith23:02:45

thank you guys. You are awesome! ­čĹĆ