89

I can see there's a sorting object, Sorting, with a quicksort method, quickSort, on it.

What would be a code example of using it, sorting an array of object of arbitrary type? It looks like I need to pass in an implementation of the Orderable trait, but I am unsure of the syntax.

Also, I would prefer answers doing this the 'Scala way'. I know I can just use a Java library.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dan Gravell
  • 7,855
  • 7
  • 41
  • 64

7 Answers7

109

With Scala 2.8 or later it is possible to do:

List(3,7,5,2).sortWith(_ < _)

that uses java.util.Arrays.sort, an implementation of quicksort.

adelarsq
  • 3,718
  • 4
  • 37
  • 47
  • 3
    Using implicit ordering, even shorter: List(3,7,5,2).sorted as per [scala.collection.immutable.LinearSeq](http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.LinearSeq) – Utgarda Jan 18 '14 at 20:15
  • How do we know sortWith is using java.util.Arrays.sort? Is there a reference for that? – deepkimo May 07 '14 at 05:22
  • @deepkimo in the source code https://github.com/scala/scala/blob/2.12.x/src/library/scala/collection/SeqLike.scala#L618 – adelarsq May 20 '14 at 20:49
  • This is a little bit different that what the original question was asking. It certainly works to create a new sorted List (or Array as posed in the original question) but sortWith returns a new object as opposed to sorting the original List or Array in place. – rphutchinson Oct 07 '14 at 12:51
63

Nowadays this one works too:

List(3,7,5,2).sorted

hendrik
  • 1,902
  • 16
  • 14
32

Sorting.quickSort declares functions for taking an Array of numbers or Strings, but I'm assuming you mean you want to sort a list of objects of your own classes?

The function I think you're looking at is

quickSort [K](a : Array[K])(implicit view$1 : (K) => Ordered[K]) : Unit

Which, if I'm reading this right, means that the objects in the Array must have the Ordered trait. So your class must extend Ordered (or must mix it in), and therefore must implement the compare method of that trait.

So to rip off an example from the book:

class MyClass(n: Int) extends Ordered[MyClass] {
   ...
  def compare(that: MyClass) =
    this.n - that.n
}

So given an Array[MyClass], then Sorting.quickSort should work.

skaffman
  • 398,947
  • 96
  • 818
  • 769
  • Yeah, that's the one. It makes sense now you point it out. How would I 'mix it in'? – Dan Gravell Jul 15 '09 at 15:09
  • See edit. You either *extend* like my example, or use "class MyClass extends OtherClass with Ordered[MyClass]", which is a mix-in. – skaffman Jul 15 '09 at 15:12
  • Thanks. I think I'll do the latter. – Dan Gravell Jul 15 '09 at 15:26
  • 6
    Not to be pedantic, but...your class need not extend Ordered, it is sufficient that there is a view (also know as an implicit conversion) in scope from your class to something that does extend Ordered. – Kim Stebel Sep 17 '10 at 18:20
19

If you just want to sort things, but aren't married to the Sorting object in particular, you can use the sort method of List. It takes a comparison function as an argument, so you can use it on whatever types you'd like:

List("Steve", "Tom", "John", "Bob").sort((e1, e2) => (e1 compareTo e2) < 0)

List(1, 4, 3, 2).sort((e1, e2) => (e1 < e2))

Lists probably qualify as "more scalaish" than arrays.

From the scala api docs:

def sort(lt : (A, A) => Boolean) : List[A]

Sort the list according to the comparison function <(e1: a, e2: a) =>

Boolean, which should be true iff e1 is smaller than e2.

Peter Recore
  • 14,037
  • 4
  • 42
  • 62
  • Hmm, so List has a sort method, but Array doesn't. How unsatisfyingly irregular. I expect that sort of nonsense from the Java API, but not Scala. – skaffman Jul 15 '09 at 15:19
  • I'm too much of a scala newb to know for sure, but it may be a necessary evil, given that Scala is maintaining compatibility with all the java stuff. On the other hand, Scala does so much magic, it seems normal enough to want it to do even more! – Peter Recore Jul 15 '09 at 16:06
  • 5
    List(...) sort { _ < _ } would be more idiomatic. – Daniel Spiewak Jul 15 '09 at 16:39
  • 2
    It's also worth noting that String does define (through an implicit conversion) the < method: "daniel" < "chris" == false – Daniel Spiewak Jul 15 '09 at 16:43
  • 11
    Just for info, on scala 2.9.1 sort seems deprecated, use sortWith – Jérôme Sep 24 '11 at 08:40
5
val array = Array((for(i <- 0 to 10) yield scala.util.Random.nextInt): _*)
scala.util.Sorting.quickSort(array)

Scala's "default" array is a mutable data structure, very close to Java's Array. Generally speaking, that means an "array" is not very Scala-ish, even as mutable data structures go. It serves a purpose, though. If array is the right data type for your need, then that is how you sort it. There are other sorting methods on object Sorting, by the way.

I think I just realized what your question is... you don't need to pass any implicit parameter (it's implicit, after all). That parameter exists to say that there must be some way to convert the type K into an Ordered[K]. These definitions already exist for Scala's classes, so you don't need them.

For an arbitrary class you can define it this way:

scala> case class Person(name: String)
defined class Person

scala> val array = Array(Person("John"), Person("Mike"), Person("Abe"))
array: Array[Person] = Array(Person(John), Person(Mike), Person(Abe))

scala> scala.util.Sorting.quickSort(array)
<console>:11: error: no implicit argument matching parameter type (Person) => Ordered[Person] was found.
       scala.util.Sorting.quickSort(array)
                                   ^
scala> class OrderedPerson(val person: Person) extends Ordered[Person] {
     | def compare(that: Person) = person.name.compare(that.name)
     | }
defined class OrderedPerson

scala> implicit def personToOrdered(p: Person) = new OrderedPerson(p)
personToOrdered: (p: Person)OrderedPerson

scala> scala.util.Sorting.quickSort(array)

scala> array
res8: Array[Person] = Array(Person(Abe), Person(John), Person(Mike))

Now, if Person was Ordered to begin with, this wouldn't be a problem:

scala> case class Person(name: String) extends Ordered[Person] {
     | def compare(that: Person) = name.compare(that.name)
     | }
defined class Person

scala> val array = Array(Person("John"), Person("Mike"), Person("Abe"))
array: Array[Person] = Array(Person(John), Person(Mike), Person(Abe))

scala>  scala.util.Sorting.quickSort(array)

scala> array
res10: Array[Person] = Array(Person(Abe), Person(John), Person(Mike))
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
4

I prefer to user Sorting util

Example :

val arr = Array(7,5,1, 9,2)

scala.util.Sorting.quickSort(arr)

please read this for more info Sorting util

Ahmad Al-Kurdi
  • 2,248
  • 3
  • 23
  • 39
  • Why a link to the old 2.9.0 API docs instead of the [current docs](http://www.scala-lang.org/api/current/scala/util/Sorting$.html)? – jwvh Dec 20 '17 at 02:33
3

While the accepted answer isn't wrong, the quicksort method provides more flexibility than that. I wrote this example for you.

import System.out.println
import scala.util.Sorting.quickSort

class Foo(x:Int) {
def get = x
}

//a wrapper around Foo that implements Ordered[Foo]
class OrdFoo(x:Foo) extends Ordered[Foo] {
def compare(that:Foo) = x.get-that.get
}
//another wrapper around Foo that implements Ordered[Foo] in a different way
class OrdFoo2(x:Foo) extends Ordered[Foo] {
def compare(that:Foo) = that.get-x.get
}
//an implicit conversion from Foo to OrdFoo
implicit def convert(a:Foo) = new OrdFoo(a)

//an array of Foos
val arr = Array(new Foo(2),new Foo(3),new Foo(1))

//sorting using OrdFoo
scala.util.Sorting.quickSort(arr)
arr foreach (a=>println(a.get))
/*
This will print:
1
2
3
*/

//sorting using OrdFoo2
scala.util.Sorting.quickSort(arr)(new OrdFoo2(_))
arr foreach (a=>println(a.get))
/*
This will print:
3
2
1
*/

This shows how implicit and explicit conversions from Foo to some class extending Ordered[Foo] can be used to get different sort orders.

Kim Stebel
  • 41,826
  • 12
  • 125
  • 142