I'm trying to sort a Clojure list (or seq, if that's what it's called) in a specific way. I would like it sorted in the priority of last item in decending order, then first item in ascending order. An example:
(def pnts '((1 2)
(2 4)
(3 2)
(4 10)
(5 3)
(6 1)
(7 2)))
(sort-by last > pnts)
;; ((4 10) (2 4) (5 3) (1 2) (3 2) (7 2) (6 1))
;; Notice how (1 2), (3 2), (7 2) are sorted. This
;; is correct and is what I want.
The (sort-by last) seems to be doing the trick, although this might be because the points are initially sorted by the first item. The way I am implementing the ASCII/CLI graphing script that I am writing the points will always be ordered like pnts was. My question is what is a command that can guarantee such a sorting preference?
PS I tried doing (sort-by (juxt last first) (juxt > <) pnts) but to no avail.
I think you were on the right track using juxt
. Assuming that your points are all numbers, you can just compose last
and -
to emulate a descending natural order on the last component.
user=> (sort-by (juxt (comp - last) first) (shuffle pnts))
((4 10) (2 4) (5 3) (1 2) (3 2) (7 2) (6 1))
Sort uses Java's sort, which is stable, so you can also just sort twice
(def pnts (shuffle '((1 2) (2 4) (3 2) (4 10) (5 3) (6 1) (7 2))))
(->> pnts (sort-by first <) (sort-by last >))
;=> ((4 10) (2 4) (5 3) (1 2) (3 2) (7 2) (6 1))
I have shuffled the input to ensure that the result is stable for arbitrary input order:
(sort #(or (> (last %1) (last %2))
(and (= (last %1) (last %2))
(< (first %1) (first %2))))
(shuffle '((1 2) (2 4) (3 2) (4 10) (5 3) (6 1) (7 2))))
=> ((4 10) (2 4) (5 3) (1 2) (3 2) (7 2) (6 1))
Here we verify, even with 1000 reorderings of the input, there is only one ordering of the output:
(count (into #{}
(repeatedly
1000
(fn [] (sort #(or (> (last %1) (last %2))
(and (= (last %1) (last %2))
(< (first %1) (first %2))))
(shuffle '((1 2) (2 4) (3 2) (4 10) (5 3) (6 1) (7 2))))))))
=> 1
Here we show that shuffle produces a large variety of orderings:
(count (into #{}
(repeatedly
1000
(fn [] (shuffle '((1 2) (2 4) (3 2) (4 10) (5 3) (6 1) (7 2)))))))
=> 899
(of course since shuffle is randomized the results will vary, but the count seems to be typically in the 850 - 950 range)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With