Làm thế nào để sắp xếp một danh sách trong Scala theo hai trường?


101

làm thế nào để sắp xếp một danh sách trong Scala theo hai trường, trong ví dụ này tôi sẽ sắp xếp theo lastName và 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)

Tôi thử những thứ như thế này

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

nhưng nó không hoạt động. Vì vậy, tôi tò mò cho một giải pháp tốt và dễ dàng.

Câu trả lời:


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

4
điều gì sẽ xảy ra nếu chúng ta muốn sắp xếp ngược lại trên lastName và sau đó sắp xếp tự nhiên trên firstName?
Sachin K

14
@SachinK: bạn phải tạo riêng của bạn Orderingcho Rowlớp và sử dụng nó với sortedphương pháp như thế này: rows.sorted(customOrdering). Bạn cũng có thể sử dụng tùy chỉnh Orderingcho Tuple2như thế này: rows.sortBy(r => (r.lastName, r.firstName))( Ordering.Tuple2(Ordering.String.reverse, Ordering.String) ).
senia

5
@SachinK: Bạn có thể triển khai customOrderingtheo Ordering[Row]cách thủ công hoặc sử dụng Ordering.bynhư sau: val customOrdering = Or Order.by ((r: Row) => (r.lastName, r.firstName)) (Đặt hàng.Tuple2 (Đặt hàng.String.reverse, Đặt hàng.String)) '
senia

1
Thông minh. Hoặc sắp xếp theo thứ tự giảm dầnrows.sortBy(r => (-r.field1, -r.field2))
Brent Faust

@BrentFaust bạn không thể sử dụng -với String. Bạn nên sử dụng Ordering::reversetheo cách này: rows.sortBy(r => (r.lastName, r.firstName))(implicitly[Ordering[(String, String)]].reverse).
senia

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

Nếu bạn muốn sắp xếp theo tên đã hợp nhất, như trong câu hỏi của bạn, hoặc

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

nếu trước tiên bạn muốn sắp xếp theo lastName, thì firstName; có liên quan đến các tên dài hơn (Wild, Wilder, Wilderman).

Nếu bạn viết

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

với 2 gạch dưới, phương thức mong đợi hai tham số:

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

1
Thứ tự của điều này có thể sẽ không giống như sắp xếp theo tên, rồi đến họ.
Marcin

1
Cụ thể, khi tên cuối cùng là độ dài diffferent
Luigi Plinge

7

Nói chung, nếu bạn sử dụng một thuật toán sắp xếp ổn định, bạn có thể chỉ cần sắp xếp theo một khóa, rồi đến khóa tiếp theo.

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

Kết quả cuối cùng sẽ được sắp xếp theo họ, sau đó bằng nhau, theo họ.


Bạn có chắc chắn rằng scala sortBysử dụng loại ổn định không? Nếu không thì câu trả lời này là vô nghĩa.
om-nom-nom

1
@ om-nom-nom: scala-lang.org/api/current/scala/util/Sorting$.html quickSort chỉ được xác định cho các loại giá trị, vì vậy có.
Marcin

1
rowslà một danh sách bất biến và sortBytrả về một giá trị mới chứ không phải thay đổi giá trị mà nó hoạt động (ngay cả trong các lớp có thể thay đổi). Vì vậy, biểu thức thứ hai của bạn chỉ là sắp xếp danh sách chưa được sắp xếp ban đầu.
Luigi Plinge

3
Scala, dưới lớp vỏ của phương thức sortBy sử dụng java.util.Arrays.sort, cho mảng đối tượng đảm bảo ổn định. Vì vậy, có, giải pháp này là chính xác. (Điều này đã được kiểm tra tại Scala 2.10)
Marcin Pieciukiewicz

1
Thật thú vị khi nghĩ về hiệu suất của điều này so với một loại đơn lẻ Bằng cách tạo ra một bộ tuple. Với cách tiếp cận này, bạn rõ ràng không phải tạo các bộ giá trị đó, nhưng với cách tiếp cận bộ giá trị, bạn chỉ phải so sánh tên với họ. Nhưng tôi cho rằng điều đó không quan trọng - nếu bạn đang viết mã quan trọng về hiệu suất, bạn không nên sử dụng sortBy chút nào!
AmigoNico

-3

Có lẽ điều này chỉ hoạt động cho một Danh sách các Tuples, nhưng

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

dường như hoạt động và là một cách đơn giản để thể hiện nó.


Nhưng không hoạt động đối với chuỗi, đó là những gì OP đang sắp xếp.
The Archetypal Paul

Câu hỏi này đã có một số câu trả lời được đón nhận không giới hạn trong danh sách các bộ giá trị. Vậy lý do đăng nó là gì?
bấm còi

@honk: Các giải pháp trước đây thực sự không hoạt động (AFAICT) trên Danh sách các Tuples. Nếu tôi không phải là một thành viên mới của Scala, có lẽ tôi sẽ hiểu cách biến những giải pháp trước đó hoạt động trong trường hợp đó, nhưng hôm nay thì không. Tôi nghĩ rằng câu trả lời của tôi có thể giúp một người mới Scala khác làm được điều tương tự mà tôi đang cố gắng làm.
spreinhardt

@ user3508605: Tôi đánh giá cao ý chí đóng góp của bạn. Tuy nhiên, ý tưởng của Stack Overflow là đưa ra các câu hỏi với các vấn đề cụ thể (như trường hợp ở đây) và câu trả lời giải quyết các vấn đề cụ thể đó (và chỉ những vấn đề đó). Câu trả lời của bạn cung cấp giải pháp cho một vấn đề khác. Do đó, đây là nơi sai để đăng nó. Nếu bạn nghĩ rằng câu trả lời của bạn có giá trị, hãy đặt một câu hỏi mới. Mô tả vấn đề tương ứng của bạn trong câu hỏi mới và sau đó đăng câu trả lời của bạn ở đó. Cuối cùng đừng quên xóa câu trả lời của bạn tại đây. Cảm ơn bạn đã hợp tác của bạn!
bấm còi

@honk: Chắc chắn rồi, tôi sẽ chuyển câu trả lời của mình sang một câu hỏi riêng. Và, nếu tôi có thể yêu cầu bạn thêm nhận xét vào câu trả lời trước đó cho câu hỏi này (từ Marcin), thì có vẻ như nó đã sai. (Tôi không có đủ điểm đáng tin cậy để có thể đăng trên đó.) Ví dụ trong câu trả lời đó chỉ cần sắp xếp đầu tiên theo một khóa và sau đó sắp xếp lại theo một khóa khác, loại bỏ hiệu quả kết quả của loại đầu tiên. Ít nhất là trong một Danh sách các Tuples nó có.
spreinhardt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.