Fork me on GitHub
#beginners
<
2019-12-13
>
dharrigan14:12:22

In Clojure, I can use (apply max-key :foo coll) which will return the entry in the collection with the greatest value of :foo. If there are multiple entries in the collection (i.e. a vector) and some of them have the same value for the key, then the last element will be returned. Is there a way to get the first element instead of the last?

bronsa14:12:49

(apply max-key :foo (rseq coll)) :)

dharrigan15:12:14

You sir, are a wizard!

dharrigan15:12:46

works wonderfully, thank you very much 🙂

Alex Miller (Clojure team)15:12:35

just watch your perf if coll can be large :)

Ike Mawira15:12:01

Just to note too, that rseq works on vectors and sorted maps only, so incase the collection varies in type, i.e can be a list etc, use reverse instead

Ike Mawira15:12:29

rseq is potentially faster (in constant time) than reverse.

mafcocinco15:12:17

On the performance front, if not all maps in the sequence will have :foo or there is some cut off value (like 0) that you don't care about, you can potentially filter the sequence prior to finding max-key. This may help manage the size of the list, depending on the density of :foo.

dharrigan15:12:34

Thanks Alex, but the collection is only about 5 or so elements max

dharrigan15:12:53

and it's a vector of maps 🙂

dharrigan15:12:38

and I filter beforehand, i.e., (every? element [:foo :anothermandatorykey])

dharrigan15:12:04

This came about because I'm maintaining a like-for-like between kotlin and clojure

dharrigan15:12:19

kotlin has a maxBy, but it returns the first element 🙂

andy.fingerhut15:12:10

Another option is to copy the source of max-key, rename it, and tweak its implementation a bit to return what you want.

mafcocinco15:12:42

I think I would prefer the rseq implementation as then there remains only one version of max-key. Seems like it would yield less surprise over time. But that's just like my opinion, man. 😉

josh_tackett15:12:20

anyone know how to escape a for loop before completion?

andy.fingerhut15:12:33

I don't know of one. The result is lazy, so if the code 'consuming' the result stops pulling new elements from it, it will not evaluate the whole result.

andy.fingerhut15:12:12

So effectively you can 'escape' by appropriate code outside of the for , but not from within.

Alex Miller (Clojure team)15:12:56

alternately, use a reduce and return reduced value. or use a loop/recur, don't recur.

Alex Miller (Clojure team)15:12:51

I guess you could use a :while or :when guard in the for

andy.fingerhut16:12:48

Hmmm, I've used :when , which lets you skip generating elements in the returned sequence, element by element, so not really a loop 'escape' if you care about avoiding the linear time of scanning through the rest of the elements. Never used :while nor seen it in the wild yet.

josh_tackett16:12:32

@U064X3EF3 How can I change the value of a conditional like continue-loop? from within the loop?

josh_tackett16:12:12

(let [continue_loop? true] (for [z [1 2 3] :while continue_loop?] ...))

Alex Miller (Clojure team)16:12:29

you need some stateful device

josh_tackett16:12:00

well I'm running these loops in multiple threads using a taskpool

josh_tackett16:12:09

so I'm a bit nervous to use an atom here

Alex Miller (Clojure team)16:12:24

why should you be nervous?

Alex Miller (Clojure team)16:12:41

I mean, I would just not use for

Alex Miller (Clojure team)16:12:02

when you want to explicitly control a loop, it's best to use loop

Alex Miller (Clojure team)16:12:10

but atoms are thread-safe, and you're using them in a per-thread context so they're totally safe (actually, more safe than they need to be for this)

josh_tackett16:12:26

ah good to know. @UQDNANLF2 ^^^

Alex Miller (Clojure team)16:12:41

since it's single threaded statefulness, you don't need any synchronization at all, even a 1-slot array would be "safe" (assuming each loop is isolated to a single thread and independent). If you're looking for external control, then different answer (but atoms would be a good option)

josh_tackett16:12:10

I have an atom external to the threads tracking usernames being used within the threads

josh_tackett16:12:26

now I'll have atoms within the threads tracking if loops should continue or not based on success already occuring

Andrew Brock16:12:03

I've used atoms within threads on several occasions and haven't encountered any errors

Alex Miller (Clojure team)17:12:02

atoms are threadsafe. If you did encounter errors, then something would be gravely wrong.

noisesmith18:12:55

> atoms within the threads tracking if loops should continue or not based on success already occuring what I find useful here is using (delay :done) or (promise) and then forcing or delivering from the thread that determines the computation is no longer needed

noisesmith18:12:07

you can use realized? to check if a delay / promise is still pending

noisesmith18:12:44

what I like about promises and delays is that they are one way switches, which reduces complexity

Andrew Brock19:12:01

Interesting... I've never seen promises in Clojure

noisesmith19:12:10

a clojure promise isn't like a js one - it's just a location that gets a value assigned exactly once (and you can test whether it's been assigned, and get its value if it has been assigned)