Як сортувати список у Scala за двома полями?


101

як сортувати список у Scala за двома полями, у цьому прикладі я буду сортувати за lastName та firstName?

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)

Я пробую такі речі

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

але це не працює. Тож мені цікаво гарне і просте рішення.

Відповіді:


216
rows.sortBy(r => (r.lastName, r.firstName))

4
що робити, якщо ми хочемо змінити сортування на lastName, а потім природне сортування на firstName?
Сачин К

14
@SachinK: ви повинні створити свій власний Orderingдля Rowкласу і використовувати його з sortedметодом , як це: rows.sorted(customOrdering). Ви можете також використовувати призначені для користувача Orderingдля Tuple2так: rows.sortBy(r => (r.lastName, r.firstName))( Ordering.Tuple2(Ordering.String.reverse, Ordering.String) ).
сенія

5
@SachinK: Ви можете реалізувати customOrderingяк Ordering[Row]вручну, так і за допомогою Ordering.byцього: val customOrdering = Ordering.by ((r: рядок) => (r.lastName, r.firstName)) (Ordering.Tuple2 (Ordering.String.reverse, Ordering.String)) `
senia

1
Відмінно. Або сортувати у порядку зменшенняrows.sortBy(r => (-r.field1, -r.field2))
Brent Faust

@BrentFaust ви не можете використовувати -з String. Ви повинні використовувати Ordering::reverseцей шлях: rows.sortBy(r => (r.lastName, r.firstName))(implicitly[Ordering[(String, String)]].reverse).
senia

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

Якщо ви хочете сортувати за об'єднаними іменами, як у вашому запитанні, або

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

якщо спочатку ви хочете сортувати за lastName, то firstName; актуально для більш довгих імен (Wild, Wilder, Wilderman).

Якщо ти пишеш

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

з двома підкресленнями, метод очікує двох параметрів:

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

1
Порядок цього, ймовірно, не буде таким самим, як сортування за іменем, потім прізвищем.
Марцін

1
Зокрема, коли прізвища різної довжини
Луїджі Плінге

7

Загалом, якщо ви використовуєте стабільний алгоритм сортування, ви можете просто сортувати за одним ключем, а потім за наступним.

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

Кінцевий результат буде відсортований за прізвищем, тоді, коли це рівне, за прізвищем.


Ви впевнені, що скала sortByвикористовує стабільний сорт? Інакше ця відповідь є безглуздою.
om-nom-nom

1
@ om-nom-nom: scala-lang.org/api/current/scala/util/Sorting$.html quickSort визначається лише для типів значень, тому так.
Марцін

1
rowsє незмінним списком і sortByповертає нове значення, а не мутування того, на якому він працює (навіть у мутаційних класах). Отже, ваш другий вираз - це лише сортування оригінального несортного списку.
Луїджі Плінге

3
Скала, під кришкою методу sortBy використовує java.util.Arrays.sort, який для масиву об'єктів гарантує стабільність. Отже, так, це рішення правильне. (Це було перевірено в Scala 2.10)
Marcin Pieciukiewicz

1
Цікаво подумати про ефективність цього порівняно з одним сортомBy, який створює кортеж. При такому підході вам, очевидно, не потрібно створювати ці кортежі, але при підході кортежу вам потрібно лише порівняти імена, де прізвища відповідають. Але я припускаю, що це не має значення - якщо ви пишете критичний для продуктивності код, ви не повинні використовувати sortBy взагалі!
AmigoNico

-3

Можливо, це працює лише для Списку кортежів, але

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

як видається, працює і є простим способом висловити це.


Але не працює для рядків, це те, що OP сортує.
Архетипний Павло

На це запитання вже є кілька добре отриманих відповідей, які не обмежуються списками кортежів. То в чому причина його публікації?
ханко

@honk: Попередні рішення насправді не працюють (AFAICT) у списку кортежів. Якби я не був новачком Скали, можливо, я зрозумів би, як перетворити ті попередні рішення, як працювати в цьому випадку, але сьогодні я цього не роблю. Я думав, що моя відповідь може допомогти іншому новачку Scala зробити те саме, що я намагався зробити.
спрейнхардт

@ user3508605: Я вдячний за вашу волю зробити свій внесок. Однак ідея переповнення стека полягає у тому, щоб мати запитання з конкретними проблемами (як це відбувається зараз) та відповіді, що стосуються цих конкретних проблем (і лише тих). Ваша відповідь забезпечує вирішення іншої проблеми. Тому це неправильне місце для публікації. Якщо ви вважаєте, що ваша відповідь цінна, то задайте нове запитання. Опишіть свою відповідну проблему в новому запитанні та опублікуйте там свою відповідь. Нарешті, не забудьте видалити свою відповідь тут. Дякую за співпрацю!
примхнути

@honk: Звичайно, я перенесу свою відповідь на окреме запитання. І якщо я міг би нав'язати вам додати коментар до попередньої відповіді на це запитання (від Марчіна), то, здається, це просто неправильно. (У мене не вистачає балів довіри, щоб я міг публікувати його.) Приклад у цій відповіді просто сортує спочатку за одним ключем, а потім знову сортує за іншим ключем, ефективно усуваючи результати першого сортування. Принаймні, у списку кортежів це є.
спрінхардт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.