A couple words on Arrays in Ruby
Bài đăng này đã không được cập nhật trong 6 năm
Chúng ta thường làm việc với mảng hàng ngày. Đối với nhiều người thì mảng khá là thân thuộc và dễ xử lý. Nhưng có một số method và behavior thú vị mà tôi muốn nói đến trong bài này. Bắt đầu ngay nhé
arr = [1, 2, 3]
arr[9] = 'foo'
p arr
result = [1, 2, 3, nil, nil, nil, nil, nil, nil, "foo"]
Ruby đã thay đổi chiều dài của mảng và điền các phần tử bị thiếu bằng giá trị nil
a = [1, 2, 3]
a.length = 3
a[9] = "f"
a.length = 10
Array creation
arr = Array.new # => []
Làm việc giống với arr = []
. Chúng ta có thể định nghĩa kích thước và giá trị default cho Array sử dụng đoạn code sau.
arr = Array.new(3, "test")
=> ["test", "test", "test"]
arr[6] = 'bar'
=> ["test", "test", "test", nil, nil, nil, "bar"]
Như chúng ta nhìn thấy, Ruby đã gán giá trị test
cho tất cả các phần tử của array trong quá trình khởi tạo. Nhưng khi nó chỉnh sửa lại chiều dài của mảng, Ruby đã sử dụng nil
như nhưng giá trị default.
Chúng ta có thể truyền 1 block để generate gía trị của mảng khi khởi tạo
arr = Array.new(3) { |i| "test-#{i}"}
=> ["test-0", "test-1", "test-2"]
Có một vấn đề cho việc gán các giá trị default của mảng. Bời vì việc cùng gán một giá trị default co tất cả các phần tử của mảng chúng ta có thể có một vấn đề như sau.
arr = Array.new(2, Hash.new)
=> [{}, {}]
arr[0][:foo] = 'bar'
=> [{:foo=>"bar"}, {:foo=>"bar"}]
Thật là thú vị khi thay đổi giá trị của phần tử thứ nhất mà giá trị của phần tử thứ 2 cũng thay đổi. Điều đó là bởi vì chúng cùng refer đến cùng một Hash Để giải quyết vấn đề này chúng ta khởi tại các Hash mới cho mỗi phần tử của mảng.
arr = Array.new(2) {Hash.new}
=> [{}, {}]
arr[0][:foo] = 'bar'
=> [{:foo=>"bar"}, {}]
Bây giờ mỗi phần tử của mảng refer đến một hash độc nhất Nếu bạn muốn gói một vài giá trị vào trong một mảng, bạn có thể sử dụng
arr = Array(3)
=> [3]
arr = Array([1, 2, 3])
=> [1, 2, 3]
Slicing
Array bao gồm Enumerable module, vì vậy chúng ta có tất cả các mothod tiện lợi như : find, map, inject, etc .
Đối với slicing, chúng ta có thể sử dụng method slice, or [], chúng sẽ làm việc như nhau.
arr = [1, 2, 3 ,4]
a[1] => 1
arr[20] => nil
nó còn cho phép bạn gọi ra với 2 tham số để lấy các phần từ trong phạm vi bạn muốn lấy ra
arr = [1, 2, 3, 4]
arr[1, 3] => [2, 3, 4]
Chúng ta có thể hiểu đoạn code trên như sau. Lấy 3 phần tử bắt đầu từ phần tử với index là 1 Còn một cách nữa để lấy nhiều giá trị của mảng cùng một lúc
arr[1..3] => [2, 3, 4]
Tất cả các trường hợp trên đều trả về một mảng mới, nhưng nếu bạn muốn thay đổi mảng gốc, Chúng ta có một mehthod là slice!
arr = [1, 2, 3]
arr.slice!(1, 2) => [2, 3]
a => [1]
Điều này đề cập đến một method được gọi là values_at nó sẽ trà về một dãy với các indices và trả về mảng bao gồm các phần tử đó
Inserting
0k, điều gì sẽ xảy ra nếu chúng ta insert giá trị từ một mảng này vào một mảng khác.
arr = [1, 2, 3]
arr.insert(1, [10, 20])
=> [1, [10, 20], 2, 3]
Đoạn code insert [10, 20
array như một giá trị thứ hai của arr. Nếu chúng ta muốn insert các giá trị trên. Chúng ta sử dụng đoạn code sau
arr.insert(1, [10, 20]).flatten!
=> [1, 10, 20, 2, 3]
Có thể nó là một cách không hiệu quả lắm vì mình phải sử dụng thêm hàm flatten! ở đây. Vì vậy chúng ta có thể sử dụng toán tử splat.
arr.insert(1, *[10, 20])
=> [1, 10, 20, 2, 3]
để thay thế một hoặc nhiều gía trị bằng các giá trị khác, chúng ta có thể sử dụng đoạn code sau
arr = [1,2,3]
arr[0..0] = [10, 20]
arr => [10, 20, 2, 3]
Comparing arrays
Để so sánh các mảng với nhau chúng ta sử dụng toán tử "spaceship" <=>
. Trong document nói rằng
Comparison — Returns an integer (-1, 0, or +1) if this array is less than, equal to, or greater than other_ary.
Chúng ta thử tìm hiểu xem cách hoạt động của nó như thế nào. Khi so sánh các mảng với nhau. Trong ruby sẽ đi so sánh từng phần tử trong mảng với nhau ví dụ
arr = [1,2,3]
another_arr = [1,2,4]
arr <=> another_arr => -1
Ruby compared: 1 từ mảng arr với 1 của mảng another_arr, tiếp theo là 2 với 2 và cuối cùng 3 nhỏ hơn 4 vì vậy nó trả về là -1 điều đó có nghĩa là arr < another_arr. nếu mỗi phần tử của mảng này bằng mỗi phần tử của mảng kia thì nó trả về là 0:
arr = [1, 2, 3]
another_arr = [1, 2, 3]
arr <=> another_arr => 0
Nếu bất kỳ một phần tử nào của mảng đầu tiên hơn hơn một phần tử tương đương ở bên mảng kia nó sẽ trả về lớn hơn
arr = [1, 5, 3]
another_arr = [1, 2, 3]
arr <=> another_arr => 1 (5 lớn hơn 2)
Nếu một phần tử nào của mảng không thể so sánh được thì kết quả trả về sẽ là nil
arr = [1, 5, 3]
another_arr = [1, "test", 3]
arr <=> another_arr => nil
Nếu kích thước của các mảng là khác nhau thì
arr = [1, 2, 3]
another_arr = [1, 2, 3, 4]
arr <=> another_arr => -1
Nếu kích thước của mảng thứ nhất nhỏ hơn kích thước của mảng thứ 2 thì return -1. Nếu ngược lại thì return +1
arr = [1, 2, 3, 4, 5]
another_arr = [1, 2]
arr <=> another_arr => 1
Union | and Intersection &
Mảng có thể bao gồm các phần tử trùng nhau.
arr = [1, 1, 2, 2]
Chúng ta có thể sử dụng method uniq! để loại bỏ các phần tử bị trùng nhau đi. Nhưng khi merge 2 mảng mà trong đó 2 mảng đó có một số phần tử giống nhau. Ví dụ
arr = ["foo", "bar"]
arr2 = ["bar", "baz"]
arr + arr2 # => ["foo", "bar", "bar", "baz"]
"bar" là phần tử bị trùng lặp khi merge 2 mảng trên với nhau vậy chúng ta sẽ sử dụng cách sau để cho mảng kết qủa không còn các phần tử bị trùng nhau
arr = ["foo", "bar"]
arr2 = ["bar", "baz"]
arr | arr2 => ["foo", "bar", "baz"]
document có ghi
Set Union — Returns a new array by joining ary with other_ary, excluding any duplicates and preserving the order from the given arrays.
Nếu chúng ta muốn lấy ra các phần tử trùng nhau của các mảng thì mình sử dụng &
method:
arr = ["foo", "bar"]
arr2 = ["bar", "baz"]
arr & arr2 => ["bar"]
Nó còn cho phép chúng ta kiểm tra xem một mảng có chứ mảng kia hay không
arr = ["foo", "bar", "baz"]
arr2 = ["bar", "baz"]
(arr & arr2) == arr2 => true
Còn có một cách check khác nữa
(arr2 - arr).empty? # => true
Ruby có sets, điều này là phù hợp hơn cho cái kia vì set:
Set implements a collection of unordered values with no duplicates. This is a hybrid of Array's intuitive inter-operation facilities and Hash's fast lookup.
Set có các phương thức sau: subset?
, superset?
, intersect?
, intersection
, etc.
Nó là dễ dàng để convert một mảng into một set và lấy ra tất các phần tử chúng ta cần
require 'set'
set = ["foo", "bar", "baz"].to_set
set2 = ["bar", "baz"].to_set
set.superset?(set2) # => true
set2.subset?(set) # => true
set.intersect?(set2) # => true
set.intersection set2 # => #<Set: {"bar", "baz"}>
Link tham khảo
All rights reserved