5

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)

Abdulrahman
  • 433
  • 4
  • 11

2 Answers2

3

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))
// )
Community
  • 1
  • 1
Peter Neyens
  • 9,770
  • 27
  • 33
1

For your example where you want to order Strings 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
      }
    } 
ka4eli
  • 5,294
  • 3
  • 23
  • 43
  • This is not what i am looking for. I want to sort the first item, ascending the second descending and so on – Abdulrahman Jul 26 '15 at 11:59
  • Are these items of the same type? – ka4eli Jul 26 '15 at 12:00
  • I added the last paragraph that says "Please, in your answer consider the following case: .... " And this is actually my use case. I want something that is not hard coded for specific length. – Abdulrahman Jul 26 '15 at 12:38
  • What do you mean by "length"? This solution is independent of array length. May be your examples of pairs and Array[Array[People]] are not connected? – ka4eli Jul 26 '15 at 12:46
  • i edited the question for the full picture. Thanks for your patience. – Abdulrahman Jul 26 '15 at 13:11