Mảng là một tập hợp các phần tử có thứ tự. Khác với biến thông thường là mỗi biến chỉ lưu một giá trị tại một thời điểm, mảng lưu nhiều giá trị khác nhau tại một thời điểm. Kiểu dữ liệu của mảng cũng là kiểu dữ liệu động, tức là một mảng có thể lưu cùng lúc một chuỗi string, một số nguyên hoặc cả một đối tượng khác. Các phần tử trong mảng ngoài giá trị ra còn được đánh số thứ tự, số thứ tự trong mảng được bắt đầu từ 0 giống như các ngôn ngữ khác.
Ví dụ:
arr = [1, 2, 3, 4, 5] arr.each do |num| puts num end
Chúng ta có một mảng tên là arr
có 5 phần tử là 5 số nguyên. Chúng ta dùng phương thức each
duyệt qua mỗi phần tử trong mảng rồi in giá trị ra màn hình.
1 2 3 4 5
Khởi tạo mảng
Mảng là một đối tượng, do đó chúng ta có thể sử dụng phương thức new
để tạo mảng.
Ví dụ 1:
arr = Array.new arr.push 1 arr.push 2 arr.push 3 arr.push 4 arr.push 5 puts arr
Trong đoạn code trên chúng ta tạo một mảng có 5 số nguyên.
arr = Array.new
Để tạo một mảng thì chúng ta dùng phương thức new
của lớp Array.
arr.push 1
Phương thức push
sẽ thêm một phần tử vào cuối mảng.
Ví dụ 2:
a1 = Array.new a2 = Array.new 3 a3 = Array.new 6, "coin" a4 = Array.new [11] puts [a1, a2, a3, a4].inspect
Phương thức new
có thể nhận một vài tham số khi khởi tạo.
a1 = Array.new
Nếu chúng ta chỉ gọi phương thức new thì Ruby sẽ tạo một mảng rỗng.
a2 = Array.new 3
Tham số đầu tiên mà phương thức này nhận là số lượng phần tử mảng, dòng code trên sẽ tạo một mảng có 3 phần tử là các đối tượng nil
.
a3 = Array.new 6, "coin"
Tham số thứ 2 là giá trị mặc định, dòng code trên sẽ tạo một mảng có 6 phần tử với mỗi phần tử là một string có giá trị là “coin”.
a4 = Array.new [11]
Hoặc chúng ta có thể khởi tạo các phần tử và giá trị luôn bằng cặp dấu ngoặc vuông []
. Dòng code trên tạo một mảng có một phần tử là số 11.
puts [a1, a2, a3, a4].inspect
Một mảng cũng có thể chứa các mảng khác, trong dòng trên chúng ta đưa 4 mảng a1, a2, a3 và a4 vào một mảng. Phương thức inspect
sẽ trả về một chuỗi mô tả các phần tử của mảng nằm trong cặp dấu []
.
[[], [nil, nil, nil], ["coin", "coin", "coin", "coin", "coin", "coin"], [11]]
Ví dụ 3:
Mỗi phần tử trong Ruby có thể mang bất kì kiểu dữ liệu nào, các phần tử cũng không cần phải có kiểu dữ liệu giống nhau.
class Empty end arr1 = [1, 2, 3, 4, 5] arr2 = [1, -1, "big", 3.4, Empty.new, arr1, :two] puts arr2.inspect
Trong ví dụ này chúng ta có mảng arr2
chứa số nguyên, chuỗi, số thực, một đối tượng lớp, một symbol
và một mảng khác.
[1, -1, "big", 3.4, #<Empty:0x2591868>, [1, 2, 3, 4, 5], :two]
Ví dụ 4:
Các mảng có thể lồng vào nhau không giới hạn.
arr = [1, 2, 3, [2, 4, 6, [11, 12]]] puts arr.length puts arr[0], arr[1] puts arr[3][0] puts arr[3][1] puts arr[3][3][0] puts arr[3][3][1] puts arr.flatten!.inspect
Trong ví dụ trên, mảng [11, 12] được lồng vào mảng [2, 4, 6], mảng [2, 4, 6] lại được lồng vào mảng [1, 2, 3].
puts arr.length
Phương thức length
sẽ trả về 4 vì một mảng lồng trong một mảng khác cũng chỉ tính là một phần tử.
puts arr[0], arr[1]
Toán tử []
sẽ lấy giá trị của các phần tử mảng tương ứng. Trong dòng code trên chúng ta in ra giá trị của phần tử thứ 0 và thứ 1.
puts arr[3][0] puts arr[3][1]
Để lấy giá trị của phần tử nằm trong mảng con bên trong thì chúng ta lại dùng thêm một toán tử []
khác nữa. Ở đây [3][0]
tức là lấy phần tử đầu tiên của phần tử thứ 4 trong mảng [2, 4, 6, [11, 12]]
(vì chỉ số được đánh từ 0).
puts arr[3][3][0] puts arr[3][3][1]
Tương tự với các phần tử mảng nằm sâu bên trong, chúng ta chỉ cần dùng toán tử []
là lấy được.
puts arr.flatten!.inspect
Phương thức flatten!
sẽ lấy các phần tử của mảng con nhập vào mảng cha và tạo thành một mảng mới.
4 1 2 2 4 11 12 [1, 2, 3, 2, 4, 6, 11, 12]
Xuất mảng ra màn hình
Có rất nhiều cách để in các phần tử mảng ra màn hình.
arr = [1, 2, 3, 4, 5] puts arr puts arr.inspect arr.each do |e| puts e end
Trong ví dụ trên chúng ta in mảng ra bằng 3 cách.
puts arr
Cách đơn giản nhất là dùng phương thức puts
hoặc print,
2 phương thức này sẽ in các phần tử mảng trên từng dòng.
puts arr.inspect
Phương thức inspect
sẽ in các phần tử mảng trong cặp dấu []
, mỗi phần tử ngăn cách nhau bởi dấu phẩy.
arr.each do |e| puts e end
Phương thức each
sẽ duyệt qua từng phần tử trong mảng, mỗi lần duyệt sẽ thực hiện các câu lệnh phía sau từ khóa do
.
1 2 3 4 5 [1, 2, 3, 4, 5] 1 2 3 4 5
Tuy xuất các phần tử mảng
Các đối tượng mảng có một số phương thức dùng để lấy các phần tử tại bất kì vị trí nào.
Ví dụ:
alpha = %w{ a b c d e f g h} puts alpha.first puts alpha.last puts alpha.at(3)
Trong ví dụ trên chúng ta dùng phương thức first
để lấy phần tử đầu tiên, phương thức last
lấy phần tử ở vị trí cuối cùng, phương thức at(i)
lấy phần tử tại vị trí thứ i
.
alpha = %w{a b c d e f g h}
Ngoài ra chúng ta còn có cách tạo nhanh một mảng chứa các phần tử là chuỗi theo cú pháp %w{},
theo cú pháp này %w{a b}
tương đương với ['a', 'b']
.
a h d
Ví dụ 2:
Thông thường chúng ta sẽ dùng toán tử []
để truy xuất phần tử mảng.
alpha = %w{ a b c d e f g h } puts alpha[0] puts alpha[-1] puts alpha[0, 3].inspect puts alpha[2..6].inspect puts alpha[2...6].inspect
Trong ví dụ trên chúng ta có 5 cách sử dụng toán tử []
để truy xuất phần tử mảng.
puts alpha[0] puts alpha[-1]
Để lấy từng phần tử thì chúng ta có thể đưa vào chỉ số mảng, chỉ số mảng trong Ruby được đánh số từ 0 và chúng ta cũng có thể đưa vào chỉ số âm để lấy phần tử từ cuối mảng.
puts alpha[0, 3].inspect
Chúng ta có thể sử dụng toán tử []
theo cú pháp [i, n]
và Ruby sẽ lấy n
phần tử từ vị trí i
. Phương thức inspect
chẳng qua là để in các phần tử mảng trong cặp dấu []
trên một dòng ngăn cách bằng dấu phẩy cho dễ đọc.
puts alpha[2..6].inspect puts alpha[2...6].inspect
Như chúng ta đã biết, [n..i]
sẽ lấy các phần tử từ vị trí n
đến vị trí i
, [n...i]
sẽ lấy các phần tử từ vị trí n
đến vị trí i - 1
.
Ví dụ 3:
alpha = %w{ a b c d e f g h} puts alpha.values_at(1..5).inspect puts alpha.values_at(1, 3, 5).inspect puts alpha.values_at(1, 3, 5, 6, 8).inspect puts alpha.values_at(-1, -3).inspect
Chúng ta có thể sử dụng phương thức values_at
để lấy nhiều phần tử tại những vị trí riêng biệt. Giá trị trả về của phương thức values_at
là một mảng chứa các phần tử được lấy ra..
puts alpha.values_at(1..5).inspect
Dòng trên sẽ trả về các phần tử từ vị trí 1 đến vị trí 5.
puts alpha.values_at(1, 3, 5).inspect
Dòng trên lấy các phần tử ở vị trí 1, 3, và 5.
puts alpha.values_at(-1, -3).inspect
Chúng ta cũng có thể đưa vào các chỉ số âm như với toán tử []
.
["b", "c", "d", "e", "f"] ["b", "d", "f"] ["b", "d", "f", "g", nil] ["h", "f"]
Ví dụ 4:
alpha = %w{ a b c d e f g h} puts alpha.slice(0) puts alpha.slice(-1) puts alpha.slice(0, 3).inspect puts alpha.slice(2..6).inspect puts alpha.slice(2...6).inspect
Mảng trong Ruby có phương thức slice
có chức năng và cách dùng y hệt như toán tử []
.
a h ["a", "b", "c"] ["c", "d", "e", "f", "g"] ["c", "d", "e", "f"]
Ví dụ 6:
alpha = %w{ a b c d e f g h} puts alpha.sample puts alpha.sample(3).inspect
Phương thức sample
có tác dụng lấy một hoặc một số phần tử tại vị trí bất kì.
b ["c", "f", "d"]
Một số thao tác trên mảng
Ví dụ 1:
arr1 = [1, 2, 3, 4, 5] arr2 = [6, 7, 8, 9, 10] puts arr1 + arr2 puts arr1.concat arr2
Chúng ta có thể nối 2 mảng vào nhau bằng toán tử + hoặc dùng phương thức concat.
Ví dụ 2:
alpha = %w{ a b c d e f} puts alpha.inspect puts "Array size: #{alpha.length}" puts "First element: #{alpha.first}" puts "Last element: #{alpha.last}" puts alpha.eql? alpha.dup puts alpha.eql? alpha.dup.delete_at(0) alpha.clear puts alpha.inspect puts alpha.empty?
Trong ví dụ trên chúng ta làm việc với một số phương thức mới.
puts "Array size: #{alpha.length}"
Chúng ta đã biết, phương thức length
cho biết số lượng phần tử trong mảng.
puts "First element: #{alpha.first}" puts "Last element: #{alpha.last}"
Phương thức first
và last
lấy phần tử đầu tiên và phần tử cuối cùng.
puts alpha.eql? alpha.dup
Phương thức eql!
so sánh 2 mảng và trả về True
nếu 2 mảng bằng nhau. Trong đoạn code trên là trả về True
vì ở đây chúng ta dùng phương thức dup
để tạo ra một mảng mới copy từ mảng gốc.
puts alpha.eql? alpha.dup.delete_at(0)
Phương thức delete_at(i)
sẽ xóa phần tử tại vị trí thứ i. Dòng trên sẽ trả về False
vì 2 mảng không giống nhau.
alpha.clear
Phương thức clear
sẽ xóa toàn bộ mảng.
puts alpha.empty?
Phương thức empty?
cho biết mảng có rỗng hay không.
["a", "b", "c", "d", "e", "f"] Array size: 6 First element: a Last element: f true false [] true
Ví dụ 3:
alpha = %w{a b c d e} alpha1 = alpha.reverse puts alpha1.inspect puts alpha.inspect alpha1 = alpha.reverse! puts alpha1.inspect puts alpha.inspect
Khi sử dụng các phương thức mà có thay đổi giá trị phần tử mảng thì chúng ta phải thêm dấu chấm ! vào cuối tên phương thức.
Phương thức reverse
sẽ đảo ngược các phần tử trong mảng, ví dụ phần tử đầu tiên đổi chỗ cho phần tử cuối cùng…
["e", "d", "c", "b", "a"] ["a", "b", "c", "d", "e"] ["e", "d", "c", "b", "a"] ["e", "d", "c", "b", "a"]
Ví dụ 4:
arr = [1, 2, 2, 2, 3, 4, 5, 8, 11] puts arr.index 2 puts arr.index 11 puts arr.rindex 2 puts arr.include? 3 puts arr.include? 10 puts arr.join '-' puts arr.uniq!.inspect
Trong ví dụ này chúng ta có thêm một số phương thức thao tác với mảng khác.
puts arr.index 2 puts arr.index 11
Phương thức index
i
trả về chỉ số của phần tử đầu tiên có giá trị i, ví dụ chúng ta có 3 phần tử có giá trị là 2 thì index 2
sẽ trả về phần tử đầu tiên có giá trị 2 là phần tử thứ 1.
puts arr.rindex 2
Phương thức rindex
cũng có tác dụng như phương thức index
nhưng tìm các phần tử từ phải qua trái.
puts arr.include? 3 puts arr.include? 10
Phương thức include?
cho biết một giá trị có tồn tại trong mảng hay không, phương thức này trả về True
nếu có và False
nếu ngược lại.
puts arr.join '-'
Phương thức join
sẽ tạo một chuỗi string có các phần tử ngăn cách nhau bởi kí tự được truyền vào, trong ví dụ trên là kí tự "-"
.
puts arr.uniq!.inspect
Phương thức uniq!
sẽ loại bỏ các phần tử có giá trị giống nhau.
1 8 3 true false 1-2-2-2-3-4-5-8-11 [1, 2, 3, 4, 5, 8, 11]
Thêm bớt phần tử
Ví dụ 1:
alpha = [] alpha.insert 0, 'E', 'F', 'G' alpha.push 'H' alpha.push 'I', 'J', 'K' alpha << 'L' << 'M' alpha.unshift 'A', 'B', 'C' alpha.insert(3, 'D') puts alpha.inspect
Để thêm một phần tử vào mảng thì có rất nhiều cách.
alpha.insert 0, 'E', 'F', 'G'
Phương thức insert
sẽ thêm các phần tử vào vị trí được chỉ định, trong đoạn code trên chúng ta thêm 3 kí tự E, F, G vào vị trí 0 nhưng vì mỗi vị trí chỉ có một phần tử nên F, G sẽ ở vị trí 1 và 2.
alpha.push 'H' alpha.push 'I', 'J', 'K'
Phương thức push
sẽ thêm phần tử vào cuối mảng. Chúng ta có thể thêm một lúc nhiều phần tử bằng ngăn cách nhau bởi dấu phẩy.
alpha << 'L' << 'M'
Toán tử <<
cũng có tác dụng như phương thức push.
alpha.unshift 'A', 'B', 'C'
Phương thức unshift
ngược lại với phương thức push
là chèn các phần tử mới vào đầu mảng.
alpha.insert(3, 'D')
Phương thức insert()
sẽ chèn phần tử vào vị trí được chỉ định.
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"]
Ví dụ 2:
alpha = %w{ a b c d e f g h} alpha.pop alpha.pop puts alpha.inspect alpha.shift alpha.shift puts alpha.inspect alpha.delete_at(0) alpha.delete('d') puts alpha.inspect puts alpha.clear puts alpha.inspect
Để xóa các phần tử ra khỏi mảng cũng có nhiều cách.
alpha.pop
Phương thức pop
xóa phần tử cuối cùng của mảng.
alpha.shift
Phương thức shift
xóa phần tử đầu tiên của mảng.
alpha.delete_at(0)
Phương thức delete_at(i)
xóa phần tử tại vị trí thứ i
.
puts alpha.clear
Phương thức clear
xóa toàn bộ mảng.
alpha.delete('d')
Phương thức delete(i)
xóa phần tử có giá tị là i
.
["a", "b", "c", "d", "e", "f"] ["c", "d", "e", "f"] ["e", "f"] []
Toán tử tập hợp
Tập hợp cũng là một danh sách các phần tử nhưng các phần tử không được phép trùng nhau.
A = [1, 2, 3, 4, 5] B = [4, 5, 6, 7, 8] union = A | B isect = A & B diff1 = A - B diff2 = B - A sdiff = (A - B) | (B - A) puts "Union: #{union}" puts "Intersection: #{isect}" puts "Difference of A - B: #{diff1}" puts "Difference of B - A: #{diff2}" puts "Symmetric: #{sdiff}"
Ruby có các toán tử có chức năng thực hiện các phép toán tập hợp trên mảng.
union = A | B
Toán tử | sẽ thực hiện phép hợp, phần tử nào cũng được lấy.
isect = A & B
Toán tử ^
thực hiện phép giao, chỉ có các phần tử cùng nằm chung 2 mảng mới được lấy.
diff1 = A - B diff2 = B - A
Toán tử "-"
thực hiện phép hiệu, phép hiệu A – B sẽ lấy các phần tử thuộc tập A và các phần tử trong tập B mà cũng có trong tập A.
sdiff = (A - B) | (B - A)
Phép hiệu đối xứng sẽ lấy các phần tử của riêng từng tập hợp, phần tử nào có mặt trong cả 2 tập hợp thì không lấy. Thực ra trong toán học không có phép toán này, ở đây mình chỉ kết hợp phép hiệu với phép giao lại thôi.
Union of arrays: [1, 2, 3, 4, 5, 6, 7, 8] Intersection of arrays: [4, 5] Difference of arrays A - B: [1, 2, 3] Difference of arrays B - A: [6, 7, 8] Symmetric difference of arrays: [1, 2, 3, 6, 7, 8]
Sắp xếp mảng
Ruby có một số phương thức sắp xếp các phần tử mảng rất mạnh.
arr = %w{ Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto } puts "#{arr.sort}" puts "#{arr.reverse}" puts "#{arr.shuffle}"
Phương thức sort
sắp xếp các phần tử từ bé đến lớn, hoặc theo thứ thự bảng chữ cái nếu các phần tử là kiểu chuỗi.
Phương thức reverse
thì sắp xếp theo thứ tự ngược lại.
Phương thức shuffle
sẽ xáo trộn vị trí các phần tử một cách ngẫu nhiên.
["Earth", "Jupiter", "Mars", "Mercury", "Neptune", "Pluto", "Saturn", "Uranus", "Venus"] ["Pluto", "Neptune", "Uranus", "Saturn", "Jupiter", "Mars", "Earth", "Venus", "Mercury"] ["Venus", "Mercury", "Uranus", "Mars", "Neptune", "Earth", "Jupiter", "Pluto", "Saturn"]