Ruby on Rails: Sử dụng select, reject, collect, inject và detect
Bài đăng này đã không được cập nhật trong 6 năm
Học Rails cũng có nghĩa là học Ruby, trong bài viết này sẽ đề cập tới một trong những vấn đề thường gặp mà mỗi lập trình viên mới code Ruby gặp phải đó là vòng lặp.
Tìm hiểu vòng lặp trong Ruby được coi như một quá trình tiến hóa nếu bạn mới code Ruby. Thường thì bạn sẽ luôn luôn tìm cách để sử dụng for
ngay lập tức để tương tác với các phần tử trong mảng, ví dụn như:
a = [1, 2, 3, 4]
for n in a
pust n
end
Sử dụng for
..in
.. là một cách đơn giản của Ruby. Tiếp theo trong sự phát triển là sử dụng một iterator để truy suất các phần tử trong một mảng. Vòng lặp for
được thay thế bằng each
, đến đây bạn đã bắt đầu làm một rubyist:
a.each do |n|
puts n
end
Tiếp theo là một vài cách tương tác vòng lặp khi có một số điều kiện logic được sử dụng, trong mỗi trường hợp sẽ sử dụng một cách phù hợp:
- tạo một danh sách phần tử từ một mảng
- tính tổng các phần tử trong một mảng
- tìm một phần tử trong mảng Hãy xem chúng ta có thể tìm thấy nhiều cách để thực hiện công việc vòng lặp một cách hợp lý
Tạo một danh sách phần tử từ một mảng sử dụng select
Đối với công việc này, chúng ta nên sử dụng select. Cách mà select làm việc rất đơn giản, nó là một vòng lặp đơn giản trên tất cả các phần tử của mảng và thi hành logic trên mỗi phần tử. Nếu biểu thức logic trả về TRUE, phần tử đó sẽ được thêm vào mảng mới để trả về cho lời gọi hàm khi hoàn thành:
a = [1, 2, 3, 4]
a.select{|n| n>2}
Kết quả trả về là hai phần tử cuối trong mảng: 3 và 4 do thỏa mãn điều kiện lớn hơn 2. Đó là các phần tử thỏa mãn điều kiện logic trong block. Có lưu ý là phương thức select
có một kết quả đối ngược lại với reject
. Nếu biểu thức logic trả về FALSE, phần tử đó sẽ được thêm vào mảng mới để trả về cho lời gọi hàm khi hoàn thành:
a = [1, 2, 3, 4]
a.reject{|n| n>2}
Kết quả trả về là mảng con [1, 2] bởi vì các phần tử này làm cho biếu thức logic trả về FALSE. Cũng có một phương thức khác gần với select và reject đó là collect, phương thức sẽ trả về kết quả của khối lệnh thay vì trả về các phần tử con ban đầu. Các ví dụ trước chúng ta trả về các phần tử dựa trên câu điều kiện trong block, có thể có trường hợp chúng ta cần bình phương các phần tử trong mảng, khi đó sử dụng collect như sau:
a = [1, 2, 3, 4]
a.collect{|n| n*n}
Kết quả trả về là một mảng mới mà mỗi phần tử mảng này là bình phương của mỗi phần tử mảng trước. Như vậy chúng ta sử dụng select, reject, và collect để trả về một mảng. Nếu bạn muốn trả về một cái gì khác, ví dụ như nối lại hay tính tổng giá trị, hãy xem inject bên dưới đây
Tính tổng các phần tử trong một mảng sử dụng inject
Khi bạn nghĩ đến việc tính tổng các phần tử trong một mảng, hãy nghĩ đến sử dụng inject. Sự khác nhau giữa select và inject là inject có thêm một biến khác để sử dụng trong block. Biến này được coi như biến tăng nạp sử dụng để lưu tổng các kết quả trả về của block mỗi lần lặp giá trị trong mảng. Ví dụ đơn giản như tính tổng các phần tử trong một mảng với nhau:
a = [1, 2, 3, 4]
a.inject{|acc,n| acc + n}
Kết quả trả về là 10, là tổng các phần tử của mảng. Logic trong block là đơn giản: cộng thêm phần tử lặp hiện tại vào biến tạm rồi gán kết quả trả về của block cho biến tạm. Nên nhớ là bạn phải sử dụng biến tạm trong mỗi vòng lặp. Nếu chúng ta không sử dụng biến tạm acc
trong mỗi vòng lặp. có nghĩa là đang sử dụng inject không đúng chức năng.
Bạn có thể sử dụng tham số với lời gọi inject để chỉ ra giá trị khởi tạo cho biến tạm:
a = [1,2,3,4]
a.inject(10) {|acc,n| acc + n}
Trong ví dụ này kết quả trả về sẽ là 20 bởi vì kết quả của vòng lặp sẽ được cộng thêm giá trị khởi tạo là 10. Nếu bạn cần trả về một string hoặc một mảng từ inject, khi đó bạn sẽ cần khởi tạo biến tạm như sau:
a = [1,2,3,4]
a.inject([]) {|acc,n| acc << n+n}
Trong ví dụ trên, n được gấp đôi giá trị và nối thêm vào kết quả biến tạm bằng toán tử <<
, giá trị biến tạm sẽ được khởi tạo bằng một mảng rỗng. Kết quả trả về là một mảng chứa các phần tử có giá trị gấp đôi các phần tử của mảng ban đầu.
Tìm một phần tử trong mảng sử dụng detect
Trong ví dụ cuối cùng là tìm một phần tử trong một mảng, chúng ta có thể sử dụng select để thực hiện mục đích này, nhưng hãy sử dụng một method khác là detect
:
a = [1,2,3,4]
a.detect {|n| n == 3}
Kết quả trả về là 3, giá trị mà chúng ta cần tìm kiếm. Nếu không có giá trị nào thỏa mãn, kết quả vòng lặp sẽ trả về là nil
Như vậy tổng kết lại có một vài điều lưu ý đáng nhớ sau khi làm việc với vòng lặp:
- sử dụng select hoặc reject nếu bạn muốn chọn ra hoặc loại đi những giá trị của mảng theo một điều kiện nào đó
- sử dụng collect nếu bạn muốn tạo một mảng dựa trên kết quả trả về từ logic trong block
- sử dụng inject nếu bạn muốn tích trữ, lấy tổng hoặc nối các giá trị mảng lại với nhau
- sử dụng detect nếu bạn muốn tìm một phần tử trong mảng
refs
All rights reserved