Расширение Seq.sortBy в Scala - PullRequest
       17

Расширение Seq.sortBy в Scala

4 голосов
/ 18 февраля 2011

Скажем, у меня есть список имен.

case class Name(val first: String, val last: String)

val names = Name("c", "B") :: Name("b", "a") :: Name("a", "B") :: Nil

Если я сейчас хочу отсортировать этот список по фамилии (и, если этого недостаточно, по имени), это легко сделать.

names.sortBy(n => (n.last, n.first))
// List[Name] = List(Name(a,B), Name(c,B), Name(b,a))

Но что, если я хотел бы отсортировать этот список на основе какого-либо другого сопоставления для строк?

К сожалению, следующее не работает:

val o  = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) }
names.sortBy(n => (n.last, n.first))(o)
// error: type mismatch;
// found   : java.lang.Object with Ordering[String]
// required: Ordering[(String, String)]
//   names.sortBy(n => (n.last, n.first))(o)

есть ли способ, позволяющий мне изменить порядок без необходимости писать явный метод sortWith с несколькими ветвями if - else, чтобы иметь дело со всеми случаями?

Ответы [ 3 ]

4 голосов
/ 18 февраля 2011

Ну, это почти делает трюк:

names.sorted(o.on((n: Name) => n.last + n.first))

С другой стороны, вы также можете сделать это:

implicit val o  = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) }
names.sortBy(n => (n.last, n.first))

Этот локально определенный неявный будет иметь приоритет надопределенный для объекта Ordering.

2 голосов
/ 18 февраля 2011

Одним из решений является расширение иначе неявно используемого порядка Tuple2. К сожалению, это означает запись Tuple2 в коде.

names.sortBy(n => (n.second, n.first))(Ordering.Tuple2(o, o))
1 голос
/ 18 февраля 2011

Я не уверен на 100%, какие методы, по вашему мнению, должны иметь collator.

Но у вас будет больше гибкости, если вы определите порядок в классе дел:

val o = new Ordering[Name]{
  def compare(a: Name, b: Name) =
    3*math.signum(collator.compare(a.last,b.last)) +
    math.signum(collator.compare(a.first,b.first))
}
names.sorted(o)

но вы также можете предоставить неявное преобразование из упорядочения строк в упорядочение имен:

def ostring2oname(os: Ordering[String]) = new Ordering[Name] {
  def compare(a: Name, b: Name) = 
    3*math.signum(os.compare(a.last,b.last)) + math.signum(os.compare(a.first,b.first))
}

, а затем вы можете использовать любой упорядочивание строк для сортировки имен:

def oo = new Ordering[String] {
  def compare(x: String, y: String) = x.length compare y.length
}
val morenames = List("rat","fish","octopus")

scala> morenames.sorted(oo)
res1: List[java.lang.String] = List(rat, fish, octopus)

Редактировать: Удобный трюк, если он не был очевидным, заключается в том, что если вы хотите упорядочить по N вещей, и вы уже используете сравнение, вы можете просто умножить каждую вещь на 3 ^ k (с первым кумножьте порядок на наибольшую степень 3) и добавьте


Если ваши сравнения занимают много времени, вы можете легко добавить каскадное сравнение:

class CascadeCompare(i: Int) {
  def tiebreak(j: => Int) = if (i!=0) i else j
}
implicit def break_ties(i: Int) = new CascadeCompare(i)

, а затем

def ostring2oname(os: Ordering[String]) = new Ordering[Name] {
  def compare(a: Name, b: Name) =
    os.compare(a.last,b.last) tiebreak os.compare(a.first,b.first)
}

(просто осторожно вложите их x tiebreak ( y tiebreak ( z tiebreak w ) ) ), чтобы не выполнять неявное преобразование несколько раз подряд).

(Если вам действительно нужно быстрое сравнениезатем вы должны выписать все это вручную или упаковать заказы в массив и использоватьпока цикл.Я предполагаю, что вы не так отчаянно нуждаетесь в производительности.)

...