6

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.

3 Answers 3

9

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))
Sign up to request clarification or add additional context in comments.

2 Comments

I am not too familiar with comp nor with juxt -- would this sort the nodes last descending then the first ascending? Your example shows it as so, but I want to be certain it will (I can't see what this example is doing).
Never mind noisesmith answered in the other question. Thanks to both, and thanks to noisesmith for the honesty :) I will be researching further into both juxt and comp.
7

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))

Comments

3

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)

9 Comments

Ahh nice. Clojure Docs clojuredocs.org/clojure_core/clojure.core/sort recommended to use sort-by when I need to access the first/last item; I assume then that sort-by is a macro for keywords n' such.
DaoWen's answer below actually solves this much more elegantly, and should probably be accepted as the best answer here.
Really? I am a Clojure beginner, and I thought that DaoWen's answer looked a bit hackish. Would his method be just as fast?
Definitely at least as fast, maybe faster. The conciseness is actually a bonus once you know the language better. It works because (comp - last) gives you a reverse order of last, and if you sort two element lists, the algorithm works by sorting by first element, then the next if the first are equivalent. So it is automatically doing what I painstakingly do by hand.
Sorting twice should be on par too (not tested). Doing two things n log n times should be about the same as doing n log n things twice.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.