12/08/2018, 16:21

A couple words on Arrays in Ruby

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"] ...

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
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]

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ử đó

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]

Để 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

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"}>

http://rubyblog.pro/2017/09/couple-words-on-arrays

0