Was ist der beste Weg, um die Sortierung in Scala umzukehren?

137

Was ist der beste Weg, um eine umgekehrte Sortierung in Scala durchzuführen? Ich stelle mir vor, dass das Folgende etwas langsam ist.

list.sortBy(_.size).reverse

Gibt es eine bequeme Möglichkeit, sortBy zu verwenden, aber eine umgekehrte Sortierung zu erhalten? Ich würde es lieber nicht brauchen sortWith.

schmmd
quelle
1
Die folgenden Lösungen sind alle sehr nett, aber ich finde Ihre ursprüngliche Art, dies zu tun, immer noch einfacher zu lesen. Ich habe bestätigt, dass diese Schreibweise einen rechnerischen Nachteil hat, wie Sie vermutet haben. Der Test, den ich gemacht habe, ist wie folgt: val clock = neuer Timer (1 bis 1e6.toInt) .sorted (Ordering [Int] .reverse) clock.addLap ("korrekter Weg") (1 bis 1e6.toInt) .sorted.reverse clock.addLap ("falscher Weg") println (clock.toString) [korrekter Weg, Runde = 76, dur. = 76] | [falscher Weg, Runde = 326, dur. = 250]
Khoa

Antworten:

241

Es kann naheliegend sein, das Vorzeichen zu ändern, wenn Sie nach einem numerischen Wert sortieren

list.sortBy(- _.size)

Im Allgemeinen kann die Sortierung nach einer Methode erfolgen, die mit einer impliziten Reihenfolge sortiert ist, die Sie explizit festlegen können, und die Reihenfolge hat eine umgekehrte Reihenfolge (nicht die umgekehrte Liste unten)

list.sorted(theOrdering.reverse)

Wenn die Reihenfolge, die Sie umkehren möchten, die implizite Reihenfolge ist, können Sie sie implizit [Bestellung [A]] (A der Typ, für den Sie bestellen) oder besser Bestellung [A] erhalten. Das wäre

list.sorted(Ordering[TheType].reverse)

sortBy ist wie die Verwendung von Ordering.by, also können Sie es tun

list.sorted(Ordering.by(_.size).reverse)

Vielleicht nicht das kürzeste zu schreiben (im Vergleich zu Minus), aber die Absicht ist klar

Aktualisieren

Die letzte Zeile funktioniert nicht. Um das _In zu akzeptieren Ordering.by(_.size), muss der Compiler wissen, für welchen Typ wir bestellen, damit er das eingeben kann _. Es mag scheinen, dass dies der Typ des Elements der Liste ist, aber dies ist nicht so, wie die Signatur von sortiert ist def sorted[B >: A](ordering: Ordering[B]). Die Reihenfolge kann aktiviert sein A, aber auch für jeden Vorfahren von A(den Sie möglicherweise verwenden byHashCode : Ordering[Any] = Ordering.by(_.hashCode)). Und tatsächlich erzwingt die Tatsache, dass die Liste kovariant ist, diese Signatur. Man kann es tun

list.sorted(Ordering.by((_: TheType).size).reverse)

aber das ist viel weniger angenehm.

Didier Dupont
quelle
Super hilfreich - ich war mir nicht sicher, ob ich eine dumme Frage stellte, aber ich habe viel aus Ihrer Antwort gelernt!
schmmd
Nur dass es nicht funktioniert. Ich sollte niemals antworten, wenn ich keine REPL zur Hand habe. Siehe Update.
Didier Dupont
Und um auf einem sekundären Feld zu sortieren, geben Sie ein Tupel zurück:list.sortBy(x => (-x.size, x.forTiesUseThisField))
Brent Faust
2
Statt list.sorted(Ordering.by((_: TheType).size).reverse)betrachtet list.sorted(Ordering.by[TheType, Int](_.size).reverse)es klarer (aber länger) für meinen Punkt veiw.
Cherry
5
Ich persönlich mag list.sortBy(_.size)(Ordering[Int].reverse)auch.
dtech
112
list.sortBy(_.size)(Ordering[Int].reverse)
incrop
quelle
27

vielleicht um es ein bisschen mehr zu verkürzen:

def Desc[T : Ordering] = implicitly[Ordering[T]].reverse

List("1","22","4444","333").sortBy( _.size )(Desc)
Bruno Bieth
quelle
19

Einfach peasy (zumindest bei size):

scala> val list = List("abc","a","abcde")
list: List[java.lang.String] = List(abc, a, abcde)

scala> list.sortBy(-_.size)
res0: List[java.lang.String] = List(abcde, abc, a)

scala> list.sortBy(_.size)
res1: List[java.lang.String] = List(a, abc, abcde)
OM Nom Nom
quelle
9

sortByhat einen impliziten Parameter, ordder die Reihenfolge bereitstellt

def sortBy [B] (f: (A) ⇒ B)(implicit ord: Ordering[B]): List[A]

So können wir ein eigenes OrderingObjekt definieren

scala> implicit object Comp extends Ordering[Int] {
 | override def compare (x: Int, y: Int): Int = y - x
 | }
defined module Comp

List(3,2,5,1,6).sortBy(x => x)
res5: List[Int] = List(6, 5, 3, 2, 1)
4e6
quelle
9

Beide sortWithund sortByhaben eine kompakte Syntax:

case class Foo(time:Long, str:String)

val l = List(Foo(1, "hi"), Foo(2, "a"), Foo(3, "X"))

l.sortWith(_.time > _.time)  // List(Foo(3,X), Foo(2,a), Foo(1,hi))

l.sortBy(- _.time)           // List(Foo(3,X), Foo(2,a), Foo(1,hi))

l.sortBy(_.time)             // List(Foo(1,hi), Foo(2,a), Foo(3,X))

Ich finde den mit sortWithleichter zu verstehen.

Jus12
quelle
8
val list = List(2, 5, 3, 1)
list.sortWith(_>_) -> res14: List[Int] = List(5, 3, 2, 1)
list.sortWith(_<_) -> res14: List[Int] = List(1, 2, 3, 5)
Tomek Kozlowski
quelle
Behandelt die Frage nicht.
Bis
1

Eine weitere Möglichkeit in Fällen, in denen Sie eine Funktion übergeben, die Sie möglicherweise nicht direkt über sortWith in einen Arraybuffer ändern können, zum Beispiel:

val buf = collection.mutable.ArrayBuffer[Int]()
buf += 3
buf += 9
buf += 1

// the sort function (may be passed through from elsewhere)
def sortFn = (A:Int, B:Int) => { A < B }

// the two ways to sort below
buf.sortWith(sortFn)                        // 1, 3, 9
buf.sortWith((A,B) => { ! sortFn(A,B) })    // 9, 3, 1
Chris
quelle
0

das ist mein code;)

val wordCounts = logData.flatMap(line => line.split(" "))
                        .map(word => (word, 1))
                        .reduceByKey((a, b) => a + b)

wordCounts.sortBy(- _._2).collect()
Anxo P.
quelle
1
Dies ist nicht falsch, aber der interessante Teil (die zweite Zeile) wird von der nicht benötigten Zeile davor übertönt. Grundsätzlich geht es hier darum, dass eine Möglichkeit zum umgekehrten Sortieren darin besteht, den Sortierwert zu negieren.
Carl-Eric Menzel