Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting lists in Clojure

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.

like image 236
user2958652 Avatar asked Jan 20 '14 19:01

user2958652


3 Answers

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))
like image 85
DaoWen Avatar answered Nov 07 '22 16:11

DaoWen


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))
like image 40
A. Webb Avatar answered Nov 07 '22 18:11

A. Webb


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)

like image 3
noisesmith Avatar answered Nov 07 '22 18:11

noisesmith