Вопрос по scala, functional-programming, sorting – Как отсортировать список в Scala по двум полям?

83

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

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

Я пробую такие вещи

<code>rows.sortBy(_.lastName + _.firstName)
</code>

но это не работает. Так что мне любопытно найти хорошее и простое решение.

Ваш Ответ

4   ответа
-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.
На этот вопрос уже есть несколько хорошо полученных ответов, которые не ограничиваются списками кортежей. Так в чем причина публикации?
@ user3508605: Я ценю ваше желание внести свой вклад. Однако идея переполнения стека состоит в том, чтобы иметь вопросы с конкретными проблемами (как это имеет место здесь) и ответы, которые решают эти конкретные проблемы (и только те). Ваш ответ дает решение для другой проблемы. Поэтому это неправильное место для публикации. Если вы считаете, что ваш ответ ценен, задайте новый вопрос. Опишите вашу соответствующую проблему в новом вопросе, а затем опубликуйте свой ответ там. Наконец, не забудьте удалить свой ответ здесь. Спасибо за ваше сотрудничество!
@honk: предыдущие решения на самом деле не работают (AFAICT) в списке кортежей. Если бы я не был новичком в Scala, возможно, я бы понял, как изменить эти предыдущие решения, чтобы работать в этом случае, но сегодня я этого не делаю. Я подумал, что мой ответ может помочь другому новичку в Scala сделать то же, что я пытался сделать.
@ honk. Конечно, я перенесу свой ответ на отдельный вопрос. И, если бы я мог навязать вам добавление комментария к предыдущему ответу на этот вопрос (от Марцина), это было бы просто неправильно. (У меня недостаточно баллов достоверности, чтобы можно было публиковать на нем.) Пример в этом ответе просто сортирует сначала по одному ключу, а затем снова сортирует по другому ключу, эффективно устраняя результаты первого рода. По крайней мере, в списке кортежей это так.
10
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)
                               ^
@Marcin: фамилия, затем имя. Да, вы правы.
Порядок этого, вероятно, не будет таким же, как сортировка по имени, а затем по фамилии.
В частности, когда фамилии имеют разную длину
@LuigiPlinge: Вы тоже правы.
5

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

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

Окончательный результат будет отсортирован по фамилии, а затем, где это совпадает, по имени.

@ом Ном ном:scala-lang.org/api/current/scala/util/Sorting$.html quickSort определен только для типов значений, так что да.
Интересно подумать о производительности этого по сравнению с единственным sortBy, который создает кортеж. При таком подходе вам, очевидно, не нужно создавать эти кортежи, но при таком подходе вам нужно сравнивать только имена, совпадающие с фамилиями. Но я полагаю, что это не имеет значения - если вы пишете код, критичный к производительности, вы вообще не должны использовать sortBy!
rows это неизменный список иsortBy возвращает новое значение, а не изменяет то, на котором оно работает (даже в изменяемых классах). Итак, ваше второе выражение просто сортирует исходный несортированный список.
Scala, под капотом метода sortBy, использует java.util.Arrays.sort, который для массива объектов гарантирует стабильность. Так что да, это правильное решение. (Это было проверено в Scala 2.10)
Вы уверены, что скалаsortBy использовать стабильную сортировку? В противном случае этот ответ не имеет смысла.
191
rows.sortBy(r => (r.lastName, r.firstName))
@SachinK: вы должны создать свой собственныйOrdering заRow класс и использовать его сsorted метод как это:rows.sorted(customOrdering), Вы также можете использовать пользовательскиеOrdering заTuple2 как это:rows.sortBy(r => (r.lastName, r.firstName))( Ordering.Tuple2(Ordering.String.reverse, Ordering.String) ).
Отлично. Или отсортировать в порядке убыванияrows.sortBy(r => (-r.field1, -r.field2))
Что если мы хотим отменить сортировку по lastName, а затем выполнить естественную сортировку по firstName?
@SachinK: Вы могли бы реализоватьcustomOrdering какOrdering[Row] вручную или используяOrdering.by как это:val customOrdering = Ordering.by ((r: Row) = & gt; (r.lastName, r.firstName)) (Ordering.Tuple2 (Ordering.String.reverse, Ordering.String)) `
@BrentFaust вы не можете использовать- сString, Вы должны использоватьOrdering::reverse сюда:rows.sortBy(r => (r.lastName, r.firstName))(implicitly[Ordering[(String, String)]].reverse).

Похожие вопросы