Fork me on GitHub

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?


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


You sir, are a wizard!


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.


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.


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


and it's a vector of maps 🙂


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


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


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


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


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. 😉


anyone know how to escape a for loop before completion?


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.


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


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.


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


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

Alex Miller (Clojure team)16:12:29

you need some stateful device


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


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)


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)


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


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.


> 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


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


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


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)