12/08/2018, 15:55

The Ultimate Guide to Ruby Sorting

Có bao nhiêu cách để sắp xếp mảng trong Ruby Mặc dù Array chỉ có 2 methods sắp xếp là sort & sort_by, nhưng các method này có thể kết hợp sử dụng với block để cho ra nhiều cách sắp xếp khác nhau. Tôi muốn chia sẻ với các bạn 1 số ví dụ trong bài viết này. Và bạn cũng có thể thực hiện một ...

Có bao nhiêu cách để sắp xếp mảng trong Ruby Mặc dù Array chỉ có 2 methods sắp xếp là sort & sort_by, nhưng các method này có thể kết hợp sử dụng với block để cho ra nhiều cách sắp xếp khác nhau. Tôi muốn chia sẻ với các bạn 1 số ví dụ trong bài viết này. Và bạn cũng có thể thực hiện một phương thức sắp xếp của bạn sử dụng thuật toán quick-sort ở cuối bài.

Basic Sorting

Method cơ bản nhất của việc sắp xếp trong Ruby là sort. Nó được định nghĩa trong Enumerable module

numbers = [5,3,2,1]
numbers.sort
# [1,2,3,5]

sort trả về kết quả là một mảng mới. Hoặc có thể dùng sort! để sửa ngay trên mảng hiện tại mà không tạo ra mảng mới.

Customized Sorting

Bây giờ chúng ta hãy xem loại sắp xếp nâng cao hơn một chút. Với sort_by

strings = %w(foo test blog a)
 
strings.sort_by(&:length)
 
# ["a", "foo", "test", "blog"]

Hoặc sort

strings = %w(foo test blog a)
 
strings.sort { |a,b| a.length <=> b.length }
 
# ["a", "foo", "test", "blog"]

Ở đây mình khuyên dùng sort_by bởi vì nhìn rõ ràng hơn và tốc độ có nhanh hơn một chút.

Reverse Sort

Để reverse sort bạn có thể sử dụng reverse method sau khi sorting. Hoặc 1 cách khác như sau.

strings = %w(foo test blog a)
 
strings.sort_by { |str| -str.length }
 
# ["blog", "test", "foo", "a"]

Alphanumeric Sorting

Bạn muốn sắp xếp 1 mảng các string có chứa numbers

music = %w(21.mp3 10.mp3 5.mp3 40.mp3)

Sử dụng method sort

music.sort
 
# ["10.mp3", "21.mp3", "40.mp3", "5.mp3"]

Đây không phải là kết qủa chúng ta mong đợi. Đây là cách chúng ta sắp xếp với trường hợp này.

music.sort_by { |s| s.scan(/d+/).first.to_i }
 
# ["5.mp3", "10.mp3", "21.mp3", "40.mp3"]

Sorting Hashes

Chúng ta cũng có thể sort trên hash tương tự như với array.

hash = {coconut: 200, orange: 50, bacon: 100}
 
hash.sort_by(&:last)
 
# [[:orange, 50], [:bacon, 100], [:coconut, 200]]

Kết quả sắp xếp đúng và trả về dạng mảng đa chiều. Chúng ta có thể chuyển về dạng hash bằng cách sử dụng Array#to_h

QuickSort Implementation

Bây giờ chúng ta sẽ implement 1 phương thức sắp xếp riêng của chúng ta, có thể sẽ chậm hơn method sort, nhưng nó sẽ khá thú vị để học.

def quick_sort(list)
  qsort_helper(list).flatten
end
 
def qsort_helper(list)
  return [] if list.empty?
 
  number        = list.sample
  lower, higher = list.partition { |n| n < number }
 
  higher.delete_at(higher.index(number))
 
  [qsort_helper(lower), number, qsort_helper(higher)]
end
 
p quick_sort [3, 7, 2, 1, 8, 12]
 
# [1, 2, 3, 7, 8, 12]

Ý tưởng của Quick sort là chọn ngẫu nhiên 1 số M trong mảng, chia mảng thành 2 phần. 1 phần có các giá trị nhỏ hơn M và 1 phần có giá trị lớn hơn hoặc bằng M. Và sẽ tiếp tục lặp lại quá trình này cho tới khi mảng được sắp xếp.

Benchmarks

Bây giờ ta sẽ so sánh tốc độ của các method sắp xếp ở phiên bản ruby 2.4.0

  sort!:                1405.8 i/s
  sort:                 1377.6 i/s - same-ish: difference falls within error
  sort_by reverse:      196.6  i/s - 7.15x  slower
  sort_by:              183.7  i/s - 7.65x  slower
  sort_by minus:        172.3  i/s - 8.16x  slower
  sort with block:      164.1  i/s - 8.57x  slower

=> sort nhanh hơn sort_by nhưng nó sẽ kém linh hoạt nếu ban không dùng block

Bài viết được dịch tại:

https://www.blackbytes.info/2017/07/ruby-sort/

0