Search code examples
sortingvectorclojure

Sorting a vector of vectors in Clojure


I want to sort this vector of vectors (of vectors) [[[5 5] [4 4] [8]] [[8 8] [2 2] [3]]] in descending order, so it becomes [[[8 8] [2 2] [3]] [[5 5] [4 4] [8]]]

For clarity, [[[5 5] [4 4] [3]] [[5 5] [4 4] [8]]] would become [[[5 5] [4 4] [8]] [[5 5] [4 4] [3]]]

The following doesn't work, not sure why:

(sort-by > [[[5 5] [4 4] [8]] [[8 8] [2 2] [3]]])

After many tries and research I'm at the same point, can someone help?


Solution

  • You’re very close, and you have already noticed that sort in Clojure is general enough to handle vectors. The problem is that sort-by with three arguments is not what you want, that is trying to use > as a key-fn that will be called on each element before sorting, and calling > with a single argument trivially returns true, so no sort is performed.

    But if you look at (doc sort) you can see that there is a three argument arity of sort itself that does what you want:

    clojure.core/sort
    ([coll] [comp coll])
      Returns a sorted sequence of the items in coll. If no comparator is
      supplied, uses compare.  comparator must implement
      java.util.Comparator.  Guaranteed to be stable: equal elements will
      not be reordered.  If coll is a Java array, it will be modified.  To
      avoid this, sort a copy of the array.
    

    Trying that using > as our comparison doesn’t work, though, because > deals with numbers, not vectors:

    (sort > [[[5 5] [4 4] [3]] [[5 5] [4 4] [8]]])
    Execution error (ClassCastException) at java.util.TimSort/countRunAndMakeAscending (TimSort.java:355).
    class clojure.lang.PersistentVector cannot be cast to class java.lang.Number (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')
    

    But we can use the compare function that was mentioned in a comment on your question with sort to achieve your goal. compare is what sort normally uses, and it can handle vectors, but it sorts in the wrong order, so we want to swap the order of the elements we are comparing:

    (sort #(compare %2 %1) [[[5 5] [4 4] [3]] [[5 5] [4 4] [8]]])
    ([[5 5] [4 4] [8]] [[5 5] [4 4] [3]])