63

is there a way in Scala to sort an array of tuples using and arbitrary comparison function? In particular I need to sort and array of tuples by their second element, but I wanted to know a general technique to sort arrays of tuples.

Thanks!

pau.estalella
  • 2,197
  • 1
  • 15
  • 20

7 Answers7

140

In scala 2.8, there is a method sortBy. Here is a simple use case:

scala> val arr = Array(("One",1),("Two",2),("Four",4),("Three",3))
arr: Array[(java.lang.String, Int)] = Array((One,1), (Two,2), (Four,4), (Three,3))

scala> arr.sortBy(_._2)
res0: Array[(java.lang.String, Int)] = Array((One,1), (Two,2), (Three,3), (Four,4))

scala>
Cory Klein
  • 51,188
  • 43
  • 183
  • 243
Eastsun
  • 18,526
  • 6
  • 57
  • 81
26

You can use this code:

scala> val v = Array(('a', 2), ('b', 1))
v: Array[(Char, Int)] = Array((a,2), (b,1))

scala> scala.util.Sorting.stableSort(v,
     | (e1: (Char, Int), e2: (Char, Int)) => e1._2 < e2._2)

scala> v
res11: Array[(Char, Int)] = Array((b,1), (a,2))

Unfortunetly, it seems that Scala cannot infer the type of the array passed to stableSort. I hope that's ok for you.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Michel Krämer
  • 14,197
  • 5
  • 32
  • 32
  • Bit nicer: `stableSort(v, (_._2 < _._2) : ((Char,Int),(Char,Int)) => Boolean)` — keeps the concerns separate, and allows one to reason about the logic and the types as independent steps, especially since the inline type signature is only a nuisance here. – Erik Kaplun Jan 12 '16 at 12:46
  • One things to note is that if you are dealing with the Any type in the sort column (suppose it has Long) you need to do: scala.util.Sorting.stableSort(v, (e1: (String, Any), e2: (String, Any)) => e1._2.asInstanceOf[Long] < e2._2.asInstanceOf[Long]) – Paul Mar 27 '20 at 19:20
9

If it's an Array, it's probably typical to use in-place sorting algorithms. However, in idiomatic Scala code, mutable collections are usually not encouraged/used. If that's the case and you have am immutable collection (or would like to not modify the Array in place), use sortWith:

scala> val a = Array(1, 3, 2, 5)
a: Array[Int] = Array(1, 3, 2, 5)

scala> a.sortWith(_ > _)
res6: Array[Int] = Array(5, 3, 2, 1)

scala> a
res7: Array[Int] = Array(1, 3, 2, 5)

sorting an Array or any other collection of tuples:

scala> val a = Array(('a', 1), ('b', 4), ('c', 5), ('d', 2))
a: Array[(Char, Int)] = Array((a,1), (b,4), (c,5), (d,2))

scala> a.sortWith(_._2 > _._2)
res4: Array[(Char, Int)] = Array((c,5), (b,4), (d,2), (a,1))

scala> a
res5: Array[(Char, Int)] = Array((a,1), (b,4), (c,5), (d,2))
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
3

On Scala 2.8 (yes, again :), you can also do this:

val v = Array(('a', 2), ('b', 1))
scala.util.Sorting.stableSort(v)(manifest[(Char, Int)], Ordering.by(_._2))

In the specific case of pairs, this can also work to sort first by the second element, and then by the first:

scala.util.Sorting.stableSort(v)(manifest[(Char, Int)], Ordering.by(_.swap))
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
2

2.7 and not in place:

(Array((2,3), (4,2), (1,5)).toList.sort (_._2 < _._2)).toArray
user unknown
  • 35,537
  • 11
  • 75
  • 121
1

You probably want def stableSort[K](a : Seq[K], f : (K, K) => Boolean) : Array[K] from scala.util.Sorting.
Your comparison function would be something like _._2 < _._1

tstenner
  • 10,080
  • 10
  • 57
  • 92
1
val l = List((2, 1), (3, 2), (0, 3))
l sort { case(a, b) => a > b }
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
Jawher
  • 6,937
  • 1
  • 17
  • 12