Quelle est la meilleure façon de trier inverse dans scala?

137

Quelle est la meilleure façon de faire un tri inverse dans scala? J'imagine que ce qui suit est un peu lent.

list.sortBy(_.size).reverse

Existe-t-il un moyen pratique d'utiliser sortBy mais d'obtenir un tri inversé? Je préfère ne pas utiliser sortWith.

schmmd
la source
1
les solutions ci-dessous sont toutes très sympas mais je trouve toujours votre façon originale de faire cela plus simple à lire. J'ai vérifié qu'il y avait un inconvénient de calcul à cette façon d'écrire comme vous le soupçonniez. Le test que j'ai fait est le suivant: val clock = new Timer (1 to 1e6.toInt) .sorted (Ordering [Int] .reverse) clock.addLap ("correct way") (1 to 1e6.toInt) .sorted.reverse clock.addLap ("chemin incorrect") println (clock.toString) [chemin correct, tour = 76, dur. = 76] | [chemin incorrect, tour = 326, dur. = 250]
Khoa

Réponses:

242

Il peut y avoir la manière évidente de changer le signe, si vous triez par une valeur numérique

list.sortBy(- _.size)

Plus généralement, le tri peut être effectué par une méthode triée avec un ordre implicite, que vous pouvez rendre explicite, et le tri a un inverse (pas la liste inversée ci-dessous).

list.sorted(theOrdering.reverse)

Si l'ordre que vous souhaitez inverser est l'ordre implicite, vous pouvez l'obtenir implicitement [Ordering [A]] (A le type sur lequel vous commandez) ou mieux Ordering [A]. Ce serait

list.sorted(Ordering[TheType].reverse)

sortBy, c'est comme utiliser Ordering.by, donc vous pouvez faire

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

Peut-être pas le plus court à écrire (par rapport au moins) mais l'intention est claire

Mettre à jour

La dernière ligne ne fonctionne pas. Pour accepter le _in Ordering.by(_.size), le compilateur doit savoir sur quel type nous commandons, afin qu'il puisse taper le _. Il peut sembler que ce serait le type de l'élément de la liste, mais ce n'est pas le cas, comme l'est la signature de trié def sorted[B >: A](ordering: Ordering[B]). L'ordre peut être activé A, mais aussi sur n'importe quel ancêtre de A(vous pourriez utiliser byHashCode : Ordering[Any] = Ordering.by(_.hashCode)). Et en effet, le fait que cette liste soit covariante force cette signature. On peut faire

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

mais c'est beaucoup moins agréable.

Didier Dupont
la source
Super utile - je n'étais pas sûr de poser une question stupide, mais j'ai beaucoup appris de votre réponse!
schmmd
Sauf que ça ne marche pas. Je ne devrais jamais répondre quand je n'ai pas de REPL sous la main. Voir la mise à jour.
Didier Dupont
Et pour trier sur un champ secondaire, retournez un tuple:list.sortBy(x => (-x.size, x.forTiesUseThisField))
Brent Faust
2
Au lieu de le list.sorted(Ordering.by((_: TheType).size).reverse)considérer list.sorted(Ordering.by[TheType, Int](_.size).reverse)plus clair (mais plus long) pour mon point de vue.
Cherry
5
J'aime personnellement list.sortBy(_.size)(Ordering[Int].reverse)aussi.
dtech
112
list.sortBy(_.size)(Ordering[Int].reverse)
incrop
la source
27

peut-être pour le raccourcir un peu plus:

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

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

Peasy facile (au moins en cas de 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
la source
9

sortBya un paramètre implicite ordqui fournit un ordre

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

donc, nous pouvons définir notre propre Orderingobjet

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
la source
9

Les deux sortWithet sortByont une syntaxe compacte:

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))

Je trouve celui avec sortWith plus facile à comprendre.

Jus12
la source
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
la source
Ne répond pas à la question.
tilde
1

Une autre possibilité dans les cas où vous passez une fonction que vous ne pourrez peut-être pas modifier directement à un Arraybuffer via sortWith par exemple:

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
la source
0

c'est mon code;)

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

wordCounts.sortBy(- _._2).collect()
Anxo P
la source
1
Ce n'est pas incorrect, mais la partie intéressante (la deuxième ligne) est noyée par la ligne inutile devant elle. Fondamentalement, le point ici est qu'une façon de faire un tri inversé consiste à annuler la valeur de tri.
Carl-Eric Menzel