2012-04-05 16 views
72

jak posortować listę w Scali według dwóch pól, w tym przykładzie posortuję według nazwy i imienia?Jak posortować listę w Scali według dwóch pól?

case class Row(var firstName: String, var lastName: String, var city: String) 

var rows = List(new Row("Oscar", "Wilde", "London"), 
       new Row("Otto", "Swift", "Berlin"), 
       new Row("Carl", "Swift", "Paris"), 
       new Row("Hans", "Swift", "Dublin"), 
       new Row("Hugo", "Swift", "Sligo")) 

rows.sortBy(_.lastName) 

próbuję rzeczy jak ten

rows.sortBy(_.lastName + _.firstName) 

ale to nie działa. Tak więc jestem ciekawy dobrego i łatwego rozwiązania.

Odpowiedz

173
rows.sortBy(r => (r.lastName, r.firstName)) 
+4

co jeśli chcemy cofnąć sortowanie na lastName, a następnie sortowanie naturalne na firstName? –

+9

@SachinK: musisz stworzyć własny 'Ordering' dla klasy' Row' i użyć go z metodą "posortowaną" w następujący sposób: 'rows.sorted (customOrdering)'. Możesz również użyć niestandardowego 'Ordering' dla' Tuple2' w następujący sposób: 'rows.sortBy (r => (r.lastName, r.firstName)) (Ordering.Tuple2 (Ordering.String.reverse, Ordering.String))' . – senia

+4

@SachinK: Możesz zaimplementować 'customOrdering' jako' Ordering [Row] 'ręcznie lub używając' Ordering.by' w ten sposób: 'val customOrdering =' Ordering.by ((r: Row) => (r.lastName, r .firstName)) (Ordering.Tuple2 (Ordering.String.reverse, Ordering.String)) ' – senia

9
rows.sortBy (row => row.lastName + row.firstName) 

Jeśli chcesz, aby posortować według połączonych nazw, jak w swoim pytaniu, czy

rows.sortBy (row => (row.lastName, row.firstName)) 

jeśli najpierw chcemy sortować według lastName, następnie firstName; odpowiednie dla dłuższych nazw (Wild, Wilder, Wilderman).

Jeśli piszesz

rows.sortBy(_.lastName + _.firstName) 

z 2 podkreśla metoda przewiduje dwa parametry:

<console>:14: error: wrong number of parameters; expected = 1 
     rows.sortBy (_.lastName + _.firstName) 
          ^
+1

Kolejność ta prawdopodobnie nie będzie taka sama jak sortowanie według imienia, a następnie nazwisko. – Marcin

+1

W szczególności, gdy nazwiska mają różne długości: –

+0

@Marcin: lastName, then firstName. Tak, masz rację. –

5

W ogóle, jeśli używasz stabilne algorytm sortowania, można po prostu sortować według jednego klucza, następnie następny.

rows.sortBy(_.firstName).sortBy(_.lastName) 

Wynik końcowy zostanie posortowany według nazwiska, a następnie, gdzie jest równy, według imienia.

+0

Czy jesteś pewien, że scala 'sortBy' używa stabilnego sortowania? W przeciwnym razie ta odpowiedź nie ma znaczenia. –

+1

@ om-nom-nom: http://www.scala-lang.org/api/current/scala/util/Sorting$.html quickSort jest zdefiniowany tylko dla typów wartości, więc tak. – Marcin

+1

'rows' jest niezmienną listą, a' sortBy' zwraca nową wartość zamiast mutować to, na którym działa (nawet w zmiennych klasach). Twoje drugie wyrażenie to po prostu sortowanie oryginalnej nieposortowanej listy. –

-4

Może to działa tylko na listę krotek, ale

scala> var zz = List((1, 0.1), (2, 0.5), (3, 0.6), (4, 0.3), (5, 0.1)) 
zz: List[(Int, Double)] = List((1,0.1), (2,0.5), (3,0.6), (4,0.3), (5,0.1)) 

scala> zz.sortBy(x => (-x._2, x._1)) 
res54: List[(Int, Double)] = List((3,0.6), (2,0.5), (4,0.3), (1,0.1), (5,0.1)) 

wydaje się działać i być w prosty sposób wyrażać.

+0

Ale nie działa dla łańcuchów, czyli tego, co sortuje OP. –

+0

To pytanie już ma kilka dobrze odebranych odpowiedzi, które nie są ograniczone do list krotek. Więc po co to publikować? – honk

+0

@honk: Poprzednie rozwiązania faktycznie nie działają (AFAICT) na liście krotek. Gdybym nie był nowicjuszem z Scala, być może zrozumiałbym, jak zmienić te wcześniejsze rozwiązania, aby pracować w tym przypadku, ale dzisiaj nie rozumiem. Pomyślałem, że moja odpowiedź może pomóc innemu nowicjuszowi Scali zrobić to samo, co próbowałem zrobić. – spreinhardt

Powiązane problemy