В Scala вам нужно найти признак Ordering[T]
, а не интерфейс Comparator
- в основном косметическая разница, так что фокус находится на атрибуте данных, а не на вещи, которая сравнивает два экземпляра данных. Для реализации признака необходимо определить метод compare(T,T)
. Очень явный вариант перечисленном сравнения может быть:
object fruitOrdering extends Ordering[String] {
def compare(lhs: String, rhs: String): Int = (lhs, rhs) match {
case ("orange", "orange") => 0
case ("orange", _) => -1
case ("apple", "orange") => 1
case ("apple", "apple") => 0
case ("apple", _) => -1
case ("grape", "orange") => 1
case ("grape", "apple") => 1
case ("grape", "grape") => 0
case ("grape", _) => -1
case ("pear", "orange") => 1
case ("pear", "apple") => 1
case ("pear", "grape") => 1
case ("pear", "pear") => 0
case ("pear", _) => -1
case _ => 0
}
}
Или, слегка адаптировать zero323's answer:
object fruitOrdering2 extends Ordering[String] {
private val values = Seq("orange", "apple", "grape", "pear")
// generate the map based off of indices so we don't have to worry about human error during updates
private val ordinalMap = values.zipWithIndex.toMap.withDefaultValue(Int.MaxValue)
def compare(lhs: String, rhs: String): Int = ordinalMap(lhs).compare(ordinalMap(rhs))
}
Теперь, когда у вас есть экземпляр Ordering[String]
, вам необходимо сообщить метод sortBy
использовать этот а не встроенный. Если вы посмотрите на подписи для RDD#sortBy
вы увидите полной подпись
def sortBy[K](f: (T) ⇒ K, ascending: Boolean = true, numPartitions: Int = this.partitions.length)(implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T]
что во втором списке параметров неявного Ordering[K]
обычно посмотрело компилятор для предопределенных порядков - вот как она знает, что естественный порядок должен быть. Однако любому неявному параметру может быть присвоено явное значение. Обратите внимание: если вы укажете одно неявное значение, то вам нужно предоставить все, поэтому в этом случае нам также необходимо предоставить ClassTag[K]
. Это всегда генерируется компилятором, но может быть легко явным образом сгенерировано с использованием scala.reflect.classTag
.
Указание все, что призывание будет выглядеть следующим образом:
import scala.reflect.classTag
rdd.sortBy { case (key, _) => key }(fruitOrdering, classOf[String])
Это все еще довольно грязно, хотя, не так ли? К счастью, мы можем использовать неявные классы, чтобы забрать много трещин. Вот отрывок, который я использую довольно часто:
package com.example.spark
import scala.reflect.ClassTag
import org.apache.spark.rdd.RDD
package object implicits {
implicit class RichSortingRDD[A : ClassTag](underlying: RDD[A]) {
def sorted(implicit ord: Ordering[A]): RDD[A] =
underlying.sortBy(identity)(ord, implicitly[ClassTag[A]])
def sortWith(fn: (A, A) => Int): RDD[A] = {
val ord = new Ordering[A] { def compare(lhs: A, rhs: A): Int = fn(lhs, rhs) }
sorted(ord)
}
}
implicit class RichSortingPairRDD[K : ClassTag, V](underlying: RDD[(K, V)]) {
def sortByKey(implicit ord: Ordering[K]): RDD[(K, V)] =
underlying.sortBy { case (key, _) => key } (ord, implicitly[ClassTag[K]])
def sortByKeyWith(fn: (K, K) => Int): RDD[(K, V)] = {
val ord = new Ordering[K] { def compare(lhs: K, rhs: K): Int = fn(lhs, rhs) }
sortByKey(ord)
}
}
}
И в действии:
import com.example.spark.implicits._
val rdd = sc.parallelize(Seq(("grape", 0.3), ("apple", 5.0), ("orange", 5.6)))
rdd.sortByKey(fruitOrdering).collect
// Array[(String, Double)] = Array((orange,5.6), (apple,5.0), (grape,0.3))
rdd.sortByKey.collect // Natural ordering by default
// Array[(String, Double)] = Array((apple,5.0), (grape,0.3), (orange,5.6))
rdd.sortWith(_._2 compare _._2).collect // sort by the value instead
// Array[(String, Double)] = Array((grape,0.3), (apple,5.0), (orange,5.6))
Удивительный ответ. благодаря – user1660256