Bagaimana cara mengurutkan daftar di Scala menurut dua bidang?

101

bagaimana cara mengurutkan daftar di Scala dengan dua bidang, dalam contoh ini saya akan mengurutkan berdasarkan nama belakang dan nama depan?

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)

Saya mencoba hal-hal seperti ini

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

tapi tidak berhasil. Jadi saya penasaran dengan solusi yang baik dan mudah.

Twistleton
sumber

Jawaban:

216
rows.sortBy(r => (r.lastName, r.firstName))
senia
sumber
4
bagaimana jika kita ingin membalikkan sortir pada lastName dan kemudian sortir alami pada firstName?
Sachin K
14
@SachinK: Anda harus membuat sendiri Orderinguntuk Rowkelas dan menggunakannya dengan sortedmetode seperti ini: rows.sorted(customOrdering). Anda juga bisa menggunakan kustom Orderinguntuk Tuple2seperti ini: rows.sortBy(r => (r.lastName, r.firstName))( Ordering.Tuple2(Ordering.String.reverse, Ordering.String) ).
senia
5
@SachinK: Anda bisa menerapkan customOrderingsebagai Ordering[Row]manual atau menggunakan Ordering.byseperti ini: val customOrdering = Ordering.by ((r: Row) => (r.lastName, r.firstName)) (Ordering.Tuple2 (Ordering.String.reverse, Ordering.String)) `
senia
1
Luar biasa. Atau untuk mengurutkan dalam urutan menurunrows.sortBy(r => (-r.field1, -r.field2))
Brent Faust
@BrentFaust tidak dapat digunakan -dengan String. Anda harus menggunakan Ordering::reversecara ini: rows.sortBy(r => (r.lastName, r.firstName))(implicitly[Ordering[(String, String)]].reverse).
senia
12
rows.sortBy (row => row.lastName + row.firstName)

Jika Anda ingin mengurutkan menurut nama yang digabungkan, seperti dalam pertanyaan Anda, atau

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

jika Anda ingin mengurutkan berdasarkan nama belakang, kemudian nama depan; relevan untuk nama yang lebih panjang (Wild, Wilder, Wilderman).

Jika Anda menulis

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

dengan 2 garis bawah, metode ini mengharapkan dua parameter:

<console>:14: error: wrong number of parameters; expected = 1
       rows.sortBy (_.lastName + _.firstName)
                               ^
Pengguna tidak diketahui
sumber
1
Urutan ini mungkin tidak akan sama dengan mengurutkan berdasarkan nama depan, lalu nama belakang.
Marcin
1
Secara khusus, ketika nama belakang memiliki panjang yang berbeda
Luigi Plinge
7

Secara umum, jika Anda menggunakan algoritme pengurutan yang stabil, Anda dapat mengurutkan berdasarkan satu kunci, lalu kunci berikutnya.

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

Hasil akhir akan diurutkan berdasarkan nama belakang, lalu di mana itu sama, dengan nama depan.

Marcin
sumber
Apakah Anda yakin bahwa scala sortBymenggunakan jenis stabil? Kalau tidak, jawaban ini tidak ada artinya.
om-nom-nom
1
@ om-nom-nom: scala-lang.org/api/current/scala/util/Sorting$.html quickSort didefinisikan hanya untuk jenis nilai, jadi ya.
Marcin
1
rowsadalah daftar yang tidak dapat diubah dan sortBymengembalikan nilai baru daripada mengubah nilai yang digunakan (bahkan dalam kelas yang dapat berubah). Jadi ekspresi kedua Anda hanya mengurutkan daftar asli yang tidak diurutkan.
Luigi Plinge
3
Scala, di bawah kap metode sortBy menggunakan java.util.Arrays.sort, yang untuk berbagai objek dijamin stabil. Jadi, ya, solusi ini benar. (Ini diperiksa di Scala 2.10)
Marcin pieciukiewicz
1
Sangat menarik untuk memikirkan tentang kinerja ini vs. satu sortBy yang menciptakan tupel. Dengan pendekatan ini Anda jelas tidak perlu membuat tupel tersebut, tetapi dengan pendekatan tupel Anda hanya perlu membandingkan nama depan yang cocok dengan nama belakang. Tapi saya rasa itu tidak masalah - jika Anda menulis kode kinerja-kritis, Anda tidak boleh menggunakan sortBy sama sekali!
AmigoNico
-3

Mungkin ini hanya berfungsi untuk Daftar Tupel, tetapi

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

tampaknya berhasil dan menjadi cara sederhana untuk mengekspresikannya.

spreinhardt
sumber
Tetapi tidak berfungsi untuk string, yang diurutkan oleh OP.
Pola Dasar Paul
Pertanyaan ini sudah memiliki beberapa jawaban yang diterima dengan baik yang tidak terbatas pada daftar tupel. Jadi apa alasan mempostingnya?
membunyikan klakson
@honk: Solusi sebelumnya sebenarnya tidak berfungsi (AFAICT) pada Daftar Tupel. Jika saya bukan pemula Scala, mungkin saya akan mengerti bagaimana mengubah solusi sebelumnya untuk bekerja dalam kasus itu, tetapi hari ini saya tidak. Saya pikir jawaban saya dapat membantu pemula Scala lain melakukan hal yang sama yang saya coba lakukan.
spreinhardt
@ user3508605: Saya menghargai keinginan Anda untuk berkontribusi. Namun, ide Stack Overflow adalah memiliki pertanyaan dengan masalah spesifik (seperti yang terjadi di sini) dan jawaban yang membahas masalah spesifik tersebut (dan hanya itu). Jawaban Anda memberikan solusi untuk masalah yang berbeda. Oleh karena itu, ini adalah tempat yang salah untuk mempostingnya. Jika menurut Anda jawaban Anda berharga, ajukan pertanyaan baru. Jelaskan masalah Anda yang sesuai di pertanyaan baru, lalu posting jawaban Anda di sana. Terakhir, jangan lupa untuk menghapus jawaban Anda di sini. Terima kasih atas kerja sama anda!
membunyikan klakson
@honk: Tentu, saya akan memindahkan jawaban saya ke pertanyaan terpisah. Dan, jika saya bisa memaksa Anda untuk menambahkan komentar pada jawaban sebelumnya untuk pertanyaan ini (dari Marcin), tampaknya itu salah. (Saya tidak memiliki cukup poin kredibilitas untuk dapat mempostingnya.) Contoh dalam jawaban itu hanya mengurutkan terlebih dahulu dengan satu kunci dan kemudian menyortir lagi dengan kunci yang berbeda, secara efektif menghilangkan hasil dari urutan pertama. Setidaknya pada Daftar Tupel yang dilakukannya.
spreinhardt