I would like to sort an array of tuples by 3rd and first element so I used the following code:
import scala.util.Sorting
val pairs = Array(("a", 5, 2), ("c", 3, 1), ("b", 1, 3))
// sort by the 3rd element, then 1st
Sorting.quickSort(pairs)(Ordering[(Int, String)].on(x => (x._3, x._1)))
My question is that, in the previous example I can sort by both the third and the first elements ascending or both of them descending (use reverse). but how to sort by the third element ascending and the 1st element descending.
Please, in your answer consider the following case:
Array[Array[People]]
where in this case I do not know the exact size of the inner array (depends on the file schema that I read into this array) and I want to sort by all the items in side (some ascending and some descending).
Edit: It looks like, i got miss understood.
Here is my full case: I have the following classes:
sealed trait GValue extends Serializable with Ordered[GValue]{
def compare(o: GValue): Int = {
o match {
case GDouble(v) => this.asInstanceOf[GDouble].v compare v
case GString(v) => this.asInstanceOf[GString].v compare v
}
}
case class GDouble(v: Double) extends GValue
case class GString(v: String) extends GValue
and i want to do a code like this.
// (startInterval, StopInterval, ArrayOfColumns)
val intervals: Array[(Long,Long,Array[GValue])] =
Array((10,20,Array(GDouble(10.2), GString("alarm"), GString("error"),GDouble("100.234"))),
(30,2000,Array(GDouble(-10.2), GString("alarm"), GString("warn"),GDouble("0.234"))))
The schema or the inner array would change based on the input file (in the example it is Double,String, String, Double but it might be Double, Double or something else). I would like to find a way to sort with out covering all the cases of the inner array (in regards to type and length), ascending and descending.
what i do currently is to change the inner array to Iterable and then use Ordering[Iterable[GValue]] for sorting or Ordering[Iterable[GValue]].reverse. But i want to sort in separated directions (ascending for the first column and then descending for the second then ascending to the third and so on)
Using Régis Jean-Gilles' CompositeOrdering
from another question, we can compose multiple Orderings
.
// Ordering to sort an Array[GValue] by column "col"
def orderByColumn(col: Int) = Ordering.by { ar: Array[GValue] => ar(col - 1) }
val `By Col3-Desc/Col1` =
CompositeOrdering(orderByColumn(3).reverse, orderByColumn(1))
val `By Col1/Col2/Col3` =
CompositeOrdering(orderByColumn(1), orderByColumn(2), orderByColumn(3))
Now we can sort an array of type Array[GValue]
, and you could sort your intervals
using sortBy
:
intervals.sortBy(_._3)(`By Col3-Desc/Col1`)
If you want to sort the intervals
array with Sorting.quickSort
, we need an Ordering
for the tuple :
type Interval = (Long, Long, Array[GValue])
implicit object tupleByArray extends Ordering[Interval] {
def compare(a: Interval, b: Interval) = a._3 compare b._3
}
Now you can sort your intervals
using Sorting.quickSort
:
implicit val arrOrd = `By Col3-Desc/Col1`
Sorting.quickSort(intervals)
// Array[(Long, Long, Array[GValue])] =
// Array(
// (30,2000,Array(GDouble(-10.2), GString(alarm), GString(warn), GDouble(0.234))),
// (10,20,Array(GDouble(10.2), GString(alarm), GString(error), GDouble(100.234)))
// )
I leave my answer from before the question was updated :
There is a great article by Eric Loots on sorting on multiple fields.
In your case of Array[People]
this could look like :
case class People(name: String, age: Int)
object PeopleOrdering {
// sort by name descending and age ascending
implicit object `By Name-Rev/Age` extends Ordering[People] {
def compare(a: People, b: People): Int = {
import scala.math.Ordered._
implicit val ord = Ordering.Tuple2[String, Int]
(b.name, a.age) compare (a.name, b.age)
}
}
}
val people = Array(People("Alice", 40), People("Bob", 50), People("Charlie", 20))
Sorting.quickSort(people)(PeopleOrdering.`By Name-Rev/Age`)
// > people
// Array[People] = Array(People(Bob,20), People(Bob,50), People(Alice,40))
val array = Array(people, Array(People("B", 1), People("C", 2)))
array.foreach(ps => Sorting.quickSort(ps)(PeopleOrdering.`By Name-Rev/Age`))
// > array
// Array[Array[People]] = Array(
// Array(People(Bob,20), People(Bob,50), People(Alice,40)),
// Array(People(C,2), People(B,1))
// )
For your example where you want to order String
s desceding add this before sorting
implicit object ReverseStringOrdering extends Ordering[String] {
def compare(x: String, y: String) = -1 * x.compareTo(y)
}
So, in general just add implicit Ordering
object of type you want to sort descending with overridden compare
method. This solution works only if you sort by different types.
If you want to sort by first item ascending and second descending add this before sorting:
implicit def AscFirstDescSecondTuple2[T1, T2](implicit ord1: Ordering[T1], ord2: Ordering[T2]): Ordering[(T1, T2)] =
new Ordering[(T1, T2)]{
def compare(x: (T1, T2), y: (T1, T2)): Int = {
val compare1 = ord1.compare(x._1, y._1)
if (compare1 != 0) return compare1
val compare2 = -1 * ord2.compare(x._2, y._2)
if (compare2 != 0) return compare2
0
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With