Sắp xếp Collection với sorted, sortBy và sortWith

Bài toán: Sắp xếp một Collection theo trình tự.

Giải pháp: Bạn có thể sử dụng sorted, sortBy hoặc sortWith
sorted có thể sắp xếp các kiểu Double, Float, Int hay một số kiểu ngầm định trong scala.math.Ordering

scala> val a = List(2, 8, 1, 5, 7).sorted
a: List[Int] = List(1, 2, 5, 7, 8)
 
scala> val b = List("pig", "cat", "dog").sorted
b: List[String] = List(cat, dog, pig) 

Giả sử chúng ta cần sắp xếp List các tuples:

val list = List(("a", 10),("c", 5),("b", 1))

Kết quả khi sorted:

scala> list.sorted
res0: List[(String, Int)] = List((a, 10), (b, 1), (c, 5))

List đã được sắp xếp theo phần tử đầu tiên của tuples. Vậy nếu muốn sắp xếp list theo phần tử thứ hai của tuples thì sẽ làm thế nào?. Ở đây chúng ta sử dụng implicit Ordering, cụ thể:

scala> list.sorted(Ordering.by[(String, Int),Int](_._2))
res1: List[(String, Int)] = List((b,1), (c, 5), (a,10))

Qua câu lệnh trên bạn cũng có thể phần nào hiểu được ý nghĩa. (String,Int), là dạng của tuples, Int là giá trị để thực hiện sorted, còn (_._2) là vị trí thứ 2 của phần tử trong tuples. Để rõ hơn, bạn hãy theo dõi ví dụ sau:

scala> val list = List(("a", 10, 10),("c", 5, 100),("b", 1, 1))
list: List[(String, Int, Int)] = List((a, 10, 10), (c, 5, 100), (b, 1, 1))

scala> list.sorted(Ordering.by[(String, Int, Int),Int](_._2))
res4: List[(String, Int, Int)] = List((b, 1, 1), (c, 5, 100), (a, 10, 10))

scala> list.sorted(Ordering.by[(String,Int,Int),Int](_._3))
res5: List[(String, Int, Int)] = List((b, 1, 1), (a, 10, 10), (c, 5, 100))

Cũng có thể sử dụng sortBy, với cú pháp ngắn gọn và dễ hiểu hơn:

scala> val list = List(("a", 10, 10),("c", 5, 100),("b", 1, 1))
list: List[(String, Int, Int)] = List((a, 10, 10), (c, 5, 100), (b, 1, 1))

scala> list.sortBy(_._2)
res6: List[(String, Int, Int)] = List((b, 1, 1), (c, 5, 100), (a, 10, 10))

scala> list.sortBy(_._3)
res7: List[(String, Int, Int)] = List((b, 1, 1), (a, 10, 10), (c, 5, 100))

Quan sát có thể thấy kết quả sắp xếp khi sử dụng sorted, sortBy, sẽ theo thứ tự từ bé đến lớn. Vậy nếu muốn kết quả theo thứ tự ngược lại thì làm thế nào?
Bạn vẫn có thể sử dụng sorted, sortBy, sau đó reverse,. Tuy nhiên, ta có một cách khác, đó là sử dụng sortWith
Với sortWith, bạn có thể sắp xếp Collection, theo cách bạn muốn

scala> List(10, 5, 8, 1, 7).sortWith(_  List(10, 5, 8, 1, 7).sortWith(_ > _)
res1: List[Int] = List(10, 8, 7, 5, 1)

scala> List("banana", "pear", "apple", "orange").sortWith(_  List("banana", "pear", "apple", "orange").sortWith(_ > _)
res3: List[String] = List(pear, orange, banana, apple)

Sắp xếp theo độ dài của các phần tử trong List:

scala> List("banana", "pear", "apple", "orange").sortWith(_.length  List("banana", "pear", "apple", "orange").sortWith(_.length > _.length)
res5: List[String] = List(banana, orange, apple, pear)
Kết luận:

Nếu kiểu của sequence không được implicit Ordering, bạn không thể sắp xếp nó với sorted.
Ví dụ:

class Person (var name: String) {
  override def toString = name
}

Nếu vẫn muốn sử dụng class Person, bạn phải mix Ordered, trait vào class Person

class Person (var name: String) extends Ordered [Person]{
  override def toString = name
  // return 0 if the same, negative if this  that
  def compare (that: Person) = {
    if (this.name == that.name) 0
        else if (this.name > that.name) 1
        else −1
    }
}

Ordered trait thực hiện các phương thức <=, , and >= , và gọi phương thức compare của bạn để so sánh chúng.

Tham khảo:

Scala Cookbook
Ordering

Add a Comment