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.


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.


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


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)

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)

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)

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.


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)

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

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

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.