Array_Mảng

tuhocvba

Administrator
Thành viên BQT
1. Khái niệm mảng (array):
Sách tin học đã quá nhiều, các bạn cũng có thể google. Tuy nhiên mình trình bày đơn giản như thế này:
Mảng là một dãy các phần tử có cùng tính chất.
Ví dụ ta có 2000 phần tử là số nguyên. Vậy thì ta sẽ khai báo mảng có kích thước là 2000 phần tử, rồi nạp giá trị số nguyên cho các phần tử của mảng.
Hoặc: Ta có 2000 phần tử là chuỗi ký tự. Vậy thì ta sẽ khai báo mảng có kích thước là 2000 phần tử, rồi nạp giá trị là chuỗi ký tự cho các phần tử của mảng.
Các phần tử chưa được nạp giá trị sẽ có giá trị là rỗng (null).
2. Các thông số cơ bản của mảng:
Kích thước mảng: Là số phần tử của mảng. Ở hình vẽ dưới đây, kích thước mảng là 6. Mảng có 6 phần tử.
Bạn cần đăng nhập để thấy đính kèm

// Hình ảnh minh họa trên có sai sót. array size = 6 là đúng.
Kiểu giá trị của mảng: Tùy thuộc vào khai báo. Thông thường hay sử dụng là String (chuỗi ký tự). Hoặc không khai báo, tùy ý xử lý sau.
Chỉ mục của mảng: Phần tử số 0, phần tử số 1, phần tử số 2,... Vậy các số 0,1,2,... là chỉ mục của mảng. Thông thường chỉ mục bắt đầu từ 0 hoặc từ 1.
Ở hình ảnh minh họa ở trên, chỉ mục bắt đầu từ 0 cho tới 5.
Chú ý, trong VBA chúng ta dùng dấu ngoặc tròn để thể hiện phần tử mảng. Ví dụ arr(5) là phần tử mảng ở chỉ mục số 5.
Mã:
Sub vidu1()
    Dim arr(0 To 5) As Integer
    Dim n1          As Integer
    Dim n2          As Integer
   
   
    arr(0) = 19
    arr(1) = 2019
    arr(2) = 2015
    arr(3) = 20
    arr(4) = 21
    arr(5) = 22
   
    n1 = LBound(arr)
    n2 = UBound(arr)
End Sub
Chương trình trên có kích thước được khai báo là từ 0 tới 5, tức kích thước mảng là 6. Chúng có kiểu giá trị là số nguyên integer.
Ta chú ý tới Lbound, là chỉ số nhỏ nhất của mảng, ở code trên, chỉ số này sẽ là 0.
Ubound là chỉ số lớn nhất của mảng, ở code này, chỉ số này sẽ là 5.
Như vậy kích thước mảng theo toán học sẽ tính bằng công thức:
Ubound - Lbound + 1
3. Dẫn giải qua các ví dụ:

Ví dụ số 1:
Kết quả của chương trình như sau:
Bạn cần đăng nhập để thấy đính kèm


Ở kết quả trên, tôi đã cho các bạn thấy Lbound được tính ra là 0 (chỉ số nhỏ nhất của mảng), Ubound là 5 (chỉ số lớn nhất của mảng).

Bây giờ câu hỏi đặt ra là: Các phần tử của mảng nếu chưa được nạp giá trị thì nó có giá trị mặc định là bao nhiêu?
Ta sẽ chặn chương trình tại vị trí nạp phần tử mảng số 3.
Bạn cần đăng nhập để thấy đính kèm

Như vậy câu trả lời là: Với dạng khai báo là số nguyên (integer), giá trị mặc định là 0. Do đó nếu chưa được nạp giá trị thì nó có giá trị là 0.
Nếu mảng được khai báo là chuỗi kí tự, và khi chưa được nạp giá trị nó sẽ như thế nào?
Ví dụ số 2:
Mã:
Sub vidu2()
    Dim arr(0 To 5) As String
    Dim n1          As Integer
    Dim n2          As Integer
   
   
    arr(0) = "hay tham gia website nhe"
    arr(1) = "tuhocvba.net"
    arr(2) = "chuc cac ban"
    arr(3) = "luon tim duoc"
    arr(4) = "nhung bai hoc"
    arr(5) = "bo ich"
   
    n1 = LBound(arr)
    n2 = UBound(arr)
End Sub
Chúng ta cùng xem kết quả dưới đây:
Bạn cần đăng nhập để thấy đính kèm


Như vậy kết quả là chuỗi kí tự rỗng "". Chúng ta hãy chú ý điều này để có cách xử lý phù hợp trong các chương trình khi sử dụng mảng.
 

Euler

Administrator
Thành viên BQT
Thực tế với excel có hàng và cột, do đó ta nghĩ ngay tới mảng hai chiều. Đây cũng là mảng thường được dùng.
Tất nhiên không chỉ là mảng hai chiều, chúng ta có thể khai báo mảng nhiều chiều, tùy vào mục đích sử dụng.
Bạn cần đăng nhập để thấy hình ảnh


Mảng hai chiều:
Chú ý lúc này muốn truy vấn các chỉ số của mảng ta phải thêm tham số.
Ví dụ: Lbound(arr) thì không còn hiểu được, phải ghi là Lbound(arr,1) để macro hiểu là đang muốn truy vấn chỉ số nhỏ nhất của chiều thứ nhất (số thứ tự hàng), và Lbound(arr,2) thì macro hiểu là đang muốn truy vấn chỉ số nhỏ nhất của chiều thứ hai (số thứ tự cột).
Ví dụ:
Mã:
Sub vidu2()
    Dim arr(0 To 3, 0 To 4) As String
    Dim d1          As Integer
    Dim d2          As Integer
    Dim n1          As Integer
    Dim n2          As Integer
    
    arr(1, 2) = 200
    
    d1 = LBound(arr, 1)
    d2 = UBound(arr, 1)
    n1 = LBound(arr, 2)
    n2 = UBound(arr, 2)
End Sub
Kết quả:
Bạn cần đăng nhập để thấy hình ảnh


Nạp giá trị từ excel vào mảng, ta được mảng hai chiều, mặc định các chỉ mục nhỏ nhất bắt đầu từ 1.
Bạn cần đăng nhập để thấy hình ảnh

Mã:
Sub Test1()
Dim Arr
Dim d1          As Integer
Dim d2          As Integer
Dim n1          As Integer
Dim n2          As Integer

Arr = Range("A1:J2").Value
    d1 = LBound(Arr, 1)
    d2 = UBound(Arr, 1)
    n1 = LBound(Arr, 2)
    n2 = UBound(Arr, 2)
End Sub
Kết quả:
Bạn cần đăng nhập để thấy hình ảnh


Hiện chưa thấy có khuyến cáo khi nạp giá trị excel vào mảng phải active workbook và sheet cần lấy, tuy nhiên để tránh nhầm lẫn, bạn nên ghi rõ workbook nào, sheet nào.
Mã:
Arr = ThisWorkbook.Sheets(2).Range("A1:J2").Value
Gán giá trị excel bằng mảng cũng tương tự, không có khuyến cáo phải active nhưng cần ghi rõ thông tin workbook và sheet để tránh nhầm lẫn:
Mã:
ThisWorkbook.Sheets(1).Range("A1:J2").Value = Arr
 

vbano1

SMod
Thành viên BQT
Xóa phần tử trong mảng:
Bài toán của tôi hôm nay là xóa một phần tử trong mảng. Nghe tới điều này, có lẽ nhiều bạn sẽ nghĩ rằng, mình cần phải khai báo lại mảng.
Nếu phần tử cần xóa nằm ở cuối danh sách (hoặc đầu danh sách) trong mảng, mọi chuyện sẽ dễ dàng.
Tôi ví dụ:
Bạn cần đăng nhập để thấy đính kèm

Nếu tôi khai báo lại bằng Redim thì kích thước mảng sẽ thay đổi nhưng giá trị mảng vốn có sẽ bị mất hết. Do đó chúng ta không dùng Redim, chúng ta dùng Redim preserve:
Mã:
 Redim preserve arr(1 to 5)
Kích thước mảng sẽ thay đổi, giá trị các phần tử của mảng như thế nào thì vẫn được giữ nguyên.

Mọi chuyện sẽ trở nên phức tạp hơn nếu phần tử mảng cần xóa nằm ở giữa danh sách.
Và như vậy chúng ta có một cách khác như sau, hi vọng có ích cho những người quan tâm:
Mã:
    Private Sub Form_Load()
    Dim arr  As New Collection
        arr.Add "Viet Nam"
        arr.Add "Nhat Ban"
        arr.Add "Cam pu chia"
        arr.Add "Han Quoc"
        MsgBox "SL: " & arr.Count & "  Key: " & arr.Item(3)
    arr.Remove 3
        MsgBox "SL: " & arr.Count & "  Key: " & arr.Item(3)
    End Sub
Kết quả:
Bạn cần đăng nhập để thấy đính kèm
 

tuhocvba

Administrator
Thành viên BQT
1. Làm việc bằng mảng trên excel.
Nếu chúng ta chỉ quan tâm tới dữ liệu mà không quan tâm tới format (định dạng cells như màu mè, kẻ ô), khi đó gán giá trị bảng tính vào mảng sẽ cho tốc độ chương trình nhanh hơn là xử lý trực tiếp trên bảng tính excel.
1.1 Active sheet cần làm việc.
Bước 1: Active sheet chúng ta muốn làm việc. Nếu không tiến hành, có thể xảy ra lỗi trong quá trình gán.
Workbooks(tên_workbook_làm_việc).Sheets(tên_sheet).Activate
1.2 Thực hiện gán giá trị cho mảng
Bước 2: Thực hiện gán.
arr = Workbooks(tên_workbook_làm_việc).Sheets(tên_sheet).Range(địa_chỉ_vùng_range).value
Ví dụ:
Mã:
Sub ganmang()
    Dim arr As Variant
    ThisWorkbook.Sheets(1).Activate
    arr = ThisWorkbook.Sheets(1).Range(Cells(3, 2), Cells(19, 3)).Value
   
End Sub
Các chỉ số của mảng sẽ bắt đầu bắt đầu từ 1.
Lbound(arr,1) = 1 'chỉ số đầu tiên của dòng
Lbound(arr,2) = 1 'chỉ số đầu tiên của cột
Ubound(arr,1) = 17 'chỉ số cuối cùng của dòng
Ubound(arr,2) = 2 'chỉ số cuối cùng của cột
Bạn cần đăng nhập để thấy đính kèm


Tương tự, ta cũng có thể làm ngược lại để gán giá trị một vùng Range:
Mã:
ThisWorkbook.Sheets(1).Range(Cells(3, 2), Cells(19, 3)).Value = arr
2. Chú ý khi gán Range = Mảng:
Chúng ta hãy xem xét ví dụ dưới đây:
Mã:
Sub gan2()
    Dim arr(1 To 17) As Integer
    Dim i As Integer
   
    For i = 1 To 17 Step 1
        arr(i) = i
    Next i
    ThisWorkbook.Sheets(1).Range(Cells(3, 2), Cells(19, 2)).Value = arr()
End Sub
Kết quả dưới đây sẽ làm các bạn bất ngờ:
Bạn cần đăng nhập để thấy đính kèm


Cái gì thế này. Giá trị của mảng hoàn toàn đúng ý đồ. Vậy tại sao gán trên cột B lại thành ra như thế này hả trời?
Chúng ta sẽ quay trở lại với vấn đề này sau. Nào bây giờ, tiếp tục thử phép gán khác nhé:
Mã:
Sub gan2b()
    Dim arr(1 To 17) As Integer
    Dim i As Integer
   
    For i = 1 To 17 Step 1
        arr(i) = i
    Next i
    ThisWorkbook.Sheets(1).Range(Cells(3, 2), Cells(3, 18)).Value = arr
End Sub
Bạn cần đăng nhập để thấy đính kèm


ồ, lần này thì mọi thứ diễn ra suôn sẻ, theo đúng ý đồ rồi. Chúng ta đã lờ mờ đoán ra vấn đề nằm ở đâu. Nó tương tự như thao tác copy và paste từ cột thành hàng.
Và ở đây, chúng ta hiểu mảng một chiều tương tự một hàng. Và khi chuyển nó thành cột ta cần phải qua một thủ thủ tục trung gian Transpose:
Mã:
Sub gan3()
    Dim arr(1 To 17) As Integer
    Dim i As Integer
   
    For i = 1 To 17 Step 1
        arr(i) = i
    Next i
    ThisWorkbook.Sheets(1).Range(Cells(3, 2), Cells(19, 2)).Value = WorksheetFunction.Transpose(arr)
End Sub
Kết quả bây giờ đã đúng như ý muốn của chúng ta:
Bạn cần đăng nhập để thấy đính kèm


Để tìm hiểu xem nó là cái gì, ta sẽ cho thêm mảng brr vào để xem đi qua Transpose nó thành cái gì nhé.
Mã:
Sub gan3b()
    Dim arr(1 To 17) As Integer
    Dim brr As Variant
   
    Dim i As Integer
   
    For i = 1 To 17 Step 1
        arr(i) = i
    Next i
    brr = WorksheetFunction.Transpose(arr) 'Mảng brr sẽ biến thành mảng hai chiều
    ThisWorkbook.Sheets(1).Range(Cells(3, 2), Cells(19, 2)).Value = brr 'Phép gán cho kết quả đúng ý đồ
End Sub
Bạn cần đăng nhập để thấy đính kèm

//Hình minh họa trên có sai sót. Đính chính: Mảng một chiều, không có chỉ số cột.
Thì ra là vậy, sau khi đi qua Transpose thì mảng một chiều trở thành mảng hai chiều, và để gán cột = mảng thì mảng đó phải là mảng hai chiều.
 

giaiphapvba

Administrator
Thành viên BQT
Như vậy, chúng ta thấy rằng, gán cho cột thì phải dùng mảng hai chiều.
Và gán cho hàng sẽ phải dùng mảng một chiều.

Thật vậy:
Ta xét ví dụ sau:
Mã:
Sub ganmang1()
    Dim arr(1 To 10, 1 To 1) As Integer
   
    Dim i As Integer
   
    For i = 1 To 10 Step 1
        arr(i, 1) = i
    Next i
    ThisWorkbook.Sheets(1).Range(Cells(1, 1), Cells(1, 10)) = arr
End Sub
Ở trên ta dùng mảng hai chiều (có một cột) và gán cho hàng. Và đây là kết quả:
Bạn cần đăng nhập để thấy đính kèm


Vấn đề tương tự như bài viết trước đã xuất hiện. Kết quả trên không như mong muốn của chúng ta.
Bây giờ ta sửa lại code:
Mã:
Sub ganmang2()
    Dim arr(1 To 10, 1 To 1) As Integer
    Dim brr As Variant
   
    Dim i As Integer
   
    For i = 1 To 10 Step 1
        arr(i, 1) = i
    Next i
    brr = WorksheetFunction.Transpose(arr) 'Biến mảng hai chiều thành mảng một chiều
    ThisWorkbook.Sheets(1).Range(Cells(1, 1), Cells(1, 10)) = brr
End Sub
Kết quả:
Bạn cần đăng nhập để thấy đính kèm


Như vậy ta đi tới kết luận:
-Hàm Transpose sẽ biến mảng một chiều thành mảng hai chiều(có một cột), và ngược lại, biến mảng hai chiều (có một cột) thành mảng một chiều.
-Gán cho cột thì phải dùng mảng hai chiều. Và gán cho hàng thì phải dùng mảng một chiều. (Điều quan trọng cần phải nhớ)


Tổng quát về Transpose: Chúng ta biết excel có lệnh copy và paste. Tuy nhiên bây giờ thay vì paste thông thường, ta paste Transpose, thì bạn sẽ thấy nó biến hình chữ nhật đứng thành nằm ngang và ngược lại. Từ đó chúng ta hiểu rằng, thực ra đó là đảo chiều mảng.
Bạn cần đăng nhập để thấy đính kèm


Thật vậy, tôi sẽ chạy code sau để kiểm tra hình dung của mình là đúng hay sai nhé.
Bạn cần đăng nhập để thấy đính kèm

Mảng brr có 2 hàng và 3 cột đúng như chúng ta dự đoán. Giá trị của hàng 1 lần lượt là : 1,2,3
Giá trị của hàng 2 lần lượt là: 4,5,6. Kết quả này hoàn toàn giống với thao tác bằng tay khi tôi thực hiện copy và paste transpose.
Vậy đến đây các bạn đã rõ về Transpose chưa nào :)
 

Euler

Administrator
Thành viên BQT
Các bài viết trên, chúng ta thấy rằng khi gán mảng arr = Range(địa chỉ range).value, chúng ta sẽ có được mảng hai chiều, các chỉ số bắt đầu ở cả chiều ngang và chiều dọc là 1: arr(1,y) dòng bắt đầu là 1, cột y bất kỳ; hoặc arr(x,1) cột bắt đầu là 1, dòng x bất kỳ.
Liên quan tới mảng, tôi muốn các bạn nhớ lại lệnh phân tách ký tự trả kết quả là mảng.
Split(Ký_tự_nguồn,Ký_tự_phân_tách)
Tôi ví dụ: Tôi có chuỗi "1,2,3,4,5,6,7,8" các ký tự được phân tách bởi dấu phẩy ",". Vậy thì tôi sẽ dùng lệnh split để tách chuỗi này ra.
Và câu hỏi bây giờ là: Phần tử đầu tiên có chỉ số trong mảng là 1 hay 0?
Câu trả lời là 0.
Hãy xem ví dụ dưới đây:
Mã:
Sub test()
    Dim str_op As String
    Dim arr As Variant
   
    str_op = "1,2,3,4,5,6,7,8"
    arr = Split(str_op, ",")
End Sub
Bạn cần đăng nhập để thấy đính kèm

Như vậy, các bạn chú ý sự khác biệt khi phân tách, chỉ số đầu tiên của mảng là 0:arr(0).
Bạn cần đăng nhập để thấy đính kèm
 
V

vothanhthu

Guest
Qua ví dụ trên, ta có thể thấy được rằng mảng có thể được tách ra khi dùng hàm Split. Vậy có trường hợp nào ngược lại để nối các phần tử trong mảng lại với nhau không, câu trả lời là có và đó là hàm Join.

Join(Mảng_cần_nối,[Dấu_phân_cách])
Trong đó:
Mảng_cần_nối: Đây là tham số bắt buộc. Mảng chứa các phần tử mang giá trị cần nối
[Dấu_phân_cách]: Ngoặc [] biểu thị cho đây là một tham số tùy chọn, có thể có hoặc có thể không. Nếu để trống, các phần tử sẽ nối với nhau bằng dấu cách. Nếu có kí tự, các phần tử sẽ nối với nhau bằng kí tự đó.

Ví dụ: Mình có mảng đặt tên là Arr gồm 5 phần tử là các chuỗi kí tự, mình sẽ dùng hàm Join để nối các phần tử này lại với nhau bằng cả 2 cách(có tham số Dấu_phân_cách và không có tham số Dấu_phân_cách)
Mã:
Sub Join_Arr()
Dim Arr(0 To 4) As String
Dim RQ_0 As String
Dim RQ_1 As String

Arr(0) = "Chung toi"
Arr(1) = "Chinh la"
Arr(2) = "Dien dan"
Arr(3) = "Tu hoc"
Arr(4) = "VBA"

RQ_0 = Join(Arr)
RQ_1 = Join(Arr, "-")
End Sub
Bạn cần đăng nhập để thấy đính kèm

Bạn cần đăng nhập để thấy đính kèm

Qua ví dụ chúng ta có thể thấy rằng, hàm Join này sẽ nối các phần tử trong mảng lại với nhau thông qua tham số Dấu_phân_cách. Thật đơn giản và dễ sử dụng.
 

tuhocvba

Administrator
Thành viên BQT
Gán giá trị cho mảng:
Nếu viết như thế này thì oải thật.
Mã:
arr(0) ="A"
arr(1) ="B"
arr(2) ="C"
Chúng ta có cách viết gọn hơn như sau:
Mã:
Sub test()
    Dim arr
    arr = Array("A", "B", "C") 
End Sub
 

Euler

Administrator
Thành viên BQT
Trong excel có sort để sắp xếp, rất nhanh.
Trong mảng thì sao đây?
Nếu là số, thuật toán sắp xếp đã có nhiều trong các sách tin học tôi không bàn nữa, nhưng các bạn lưu ý, đã từng có phàn nàn về tốc độ xử lý sắp xếp chậm nếu số lượng phần tử trong mảng lên tới khoảng 2000 phần tử, ấy là chưa nói tới việc sắp xếp chạy vòng lặp nhiều lần, và chúng ta hãy nhớ tới topic sau nếu chúng ta cần sắp xếp mảng là số theo thứ tự tăng/giảm:

Tuy nhiên nếu là alphabe (A~Z), tức là giá trị của các phần tử của mảng là ký tự, chúng ta sắp xếp như thế nào đây? Không lẽ phải ghi ra excel để sort rồi nạp lại vào mảng? Hôm nay, xin đưa ra một giải pháp xử lý sắp xếp này trên mảng:
Mã:
Sub RunTheSortMacro()

Dim i As Long
Dim myArray As Variant

'Set the array
myArray = Array("p", "A", "G", 3, "l", "6", 10, "K", 7)

'myArray variable set to the result of SortArrayAtoZ function
myArray = SortArrayAtoZ(myArray)

'Output the Array through a message box
For i = LBound(myArray) To UBound(myArray)
    MsgBox myArray(i)
Next i

End Sub
Phần code chính:
Sort the array A-Z
Mã:
Function SortArrayAtoZ(myArray As Variant)

Dim i As Long
Dim j As Long
Dim Temp

'Sort the Array A-Z
For i = LBound(myArray) To UBound(myArray) - 1
    For j = i + 1 To UBound(myArray)
        If UCase(myArray(i)) > UCase(myArray(j)) Then
            Temp = myArray(j)
            myArray(j) = myArray(i)
            myArray(i) = Temp
        End If
    Next j
Next i

SortArrayAtoZ = myArray

End Function
Sort the array Z-A
Mã:
Function SortArrayZtoA(myArray As Variant)

Dim i As Long
Dim j As Long
Dim Temp

'Sort the Array Z-A
For i = LBound(myArray) To UBound(myArray) - 1
    For j = i + 1 To UBound(myArray)
        If UCase(myArray(i)) < UCase(myArray(j)) Then
            Temp = myArray(j)
            myArray(j) = myArray(i)
            myArray(i) = Temp
        End If
    Next j
Next i

SortArrayZtoA = myArray

End Function
Nguồn tham khảo:
 

giaiphapvba

Administrator
Thành viên BQT
Chèn thêm (hoặc xóa) phần tử vào vị trí bất kỳ trong mảng.
1. Đặt vấn đề :
Nói tới chủ đề này nhiều người sẽ nghĩ ngay tới . Tuy nhiên nó chỉ giúp chúng ta thêm bớt phần tử vào cuối mảng. Nếu vị trí muốn thêm bớt nằm ở giữa mảng thì sao? Đây là vấn đề vẫn luôn làm đau đầu nhiều người, vì trong VBA không cung cấp cho chúng ta công cụ này. Do đó, chỉ còn cách là tự xây dựng hàm hay thủ tục riêng để thực hiện điều này.
2. Thêm phần tử vào vị trí bất kỳ trong mảng:
Mã:
Sub InsertToArray(a_Ary, a_Data, a_iPosition)
    Dim i                       '// Dùng để chạy vòng lặp
    Dim iCount                  '// Số phần tử của mảng
    
    '// Số phần tử của mảng, ta muốn thêm 1 phần tử vào mảng cho nên:
    iCount = UBound(a_Ary) + 1
    
    '// Lưu lại giá trị vốn có của mảng
    ReDim Preserve a_Ary(iCount)
    
    '// Trường hợp vị trí muốn thêm phần tử mới vượt ra ngoài phạm vi kích thước mảng:
    If a_iPosition > iCount Then
        a_iPosition = iCount
    End If
    
    '// Từ vị trí cuối của mảng tới vị trí thêm phần tử mới:
    For i = iCount To a_iPosition + 1 Step -1
        '// Lưu lại giá trị vốn có từ vị trí i-1 trở về vị trí đầu tiên trong mảng a_Ary
        Call SetValue(a_Ary(i - 1), a_Ary(i))
    Next
    
    '// Thêm dữ liệu a_Data vào vị trí a_iPosition của mảng a_Ary
    Call SetValue(a_Data, a_Ary(a_iPosition))
End Sub
3. Xóa vị trí bất kỳ trong mảng:
Mã:
Sub EraseToArray(a_Ary, a_iPosition)
    Dim i                       '// Thực hiện vòng lặp
    Dim iCount                  '// Số phần tử của mảng
    
    '// Lấy số phần tử của mảng
    iCount = UBound(a_Ary)
    
    '// Từ vị trí chỉ định cho tới ngay trước vị trí kết thúc của mảng:
    For i = a_iPosition To iCount - 1
        Call SetValue(a_Ary(i + 1), a_Ary(i))
    Next
    
    '// Xóa một phần tử:
    ReDim Preserve a_Ary(iCount - 1)
End Sub
4. Hàm copy dữ liệu:
Trong mục 2 và mục 3 đã trình bày ở trên, dữ liệu đầu vào của mảng là cái gì cũng OK. Có nghĩa là mảng đó là integer hay string, hay Range, hay là kiểu Dictionary thì cũng đều hoạt động.
Tuy nhiên có sự khác biệt nếu biến là kiểu Object hoặc không phải Object. Nếu biến là Object thì phép gán dữ liệu phải dùng Set.
Nếu bạn không nhớ kiến thức này, xin xem lại .
Do đó, chúng ta cần xây dựng hàm copy dữ liệu có thể làm được viện toàn năng, là Object hay không phải là Object thì cũng chẳng quan tâm, tôi vẫn thực hiện copy được dữ liệu cho bạn.
Mã:
Sub SetValue(a_From, a_To)
    Dim bType   As Boolean      '// Kiểu dữ liệu của mảng là Object hay không?
    
    '// Kiểm tra kiểu dữ liệu là Object hay không
    bType = IsObject(a_From)
    
    '// Là Object thì:
    If bType = True Then
        Set a_To = a_From
    '// Không phải là Object thì:
    Else
        a_To = a_From
    End If
End Sub
5. Ví dụ về cách sử dụng:
Kiểu dữ liệu là String hay là Range thì cũng chẳng quan tâm. Ta sẽ thực hiện thêm dữ liệu vào vị trí tùy ý trong mảng. Sau đó ta sẽ thực hiện xóa dữ liệu ở vị trí tùy ý trong mảng:
Mã:
Sub InsertEraseToArrayTest()
    Dim arString()  As String   '// Mảng có kiểu dữ liệu là String
    Dim arRange()   As Range    '// Mảng có kiểu dữ liệu là Range
    Dim i           As Long     '// Thực hiện vòng lặp
    
    '// Khởi tạo mảng
    ReDim arString(7)
    ReDim arRange(7)
    
    arString(0) = "a"
    arString(1) = "b"
    arString(2) = "c"
    arString(3) = "d"
    arString(4) = "e"
    arString(5) = "f"
    arString(6) = "g"
    arString(7) = "h"
    
    Set arRange(0) = Range("A1")
    Set arRange(1) = Range("A2")
    Set arRange(2) = Range("A3")
    Set arRange(3) = Range("A4")
    Set arRange(4) = Range("A5")
    Set arRange(5) = Range("A6")
    Set arRange(6) = Range("A7")
    Set arRange(7) = Range("A8")
    
    '// Thêm phần tử mới vào mảng vào vị trí thứ 1 trong mảng string, vào vị trí 0 trên mảng Range
    Call InsertToArray(arString, "abc", 1)
    Call InsertToArray(arRange, Range("G100"), 0)
    
    '// Ghi kết quả với mảng String:
    For i = 0 To UBound(arString)
        Debug.Print CStr(i) & " - " & arString(i)
    Next
    
    '// Ghi kết quả với mảng Range
    For i = 0 To UBound(arRange)
        Debug.Print CStr(i) & " - " & arRange(i).Address(False, False)
    Next

    Debug.Print ""
    
    '// Xóa dữ liệu
    Call EraseToArray(arString, 1)
    Call EraseToArray(arRange, 0)
    
    '// Ghi kết quả với mảng String
    For i = 0 To UBound(arString)
        Debug.Print CStr(i) & " - " & arString(i)
    Next
    
    '// Ghi kết quả với mảng Range
    For i = 0 To UBound(arRange)
        Debug.Print CStr(i) & " - " & arRange(i).Address(False, False)
    Next
End Sub
Kết quả:
Mã:
0 - a
1 - abc
2 - b
3 - c
4 - d
5 - e
6 - f
7 - g
8 - h
0 - G100
1 - A1
2 - A2
3 - A3
4 - A4
5 - A5
6 - A6
7 - A7
8 - A8

0 - a
1 - b
2 - c
3 - d
4 - e
5 - f
6 - g
7 - h
0 - A1
1 - A2
2 - A3
3 - A4
4 - A5
5 - A6
6 - A7
7 - A8
Nguồn tham khảo và dịch:
 

tuhocvba

Administrator
Thành viên BQT
Lấy các phần tử của mảng mà chúng chứa chuỗi ký tự mà ta chỉ định bằng Filter:
Cấu trúc:
Function Filter(SourceArray, Match As String, [Include As Boolean = True], [Compare As VbCompareMethod = vbBinaryCompare]) As Variant
SourceArrayĐối tượng mảng mà chúng ta thực thi tìm kiếm trong đó. Chú ý chỉ dùng được mảng một chiều. Nếu không phải mảng một chiều thì sẽ lỗi.
MatchChỉ định chuỗi ký tự (từ khóa) tìm kiếm
Include (có thể giản lược)Tìm phần tử mảng chứa từ khóa tìm kiếm thì để là True. Nếu không chứa từ khóa tìm kiếm thì để là False. Nếu giản lược, tham số này mặc định là True.
Compare (Chế độ so sánh-có thể giản lược)
Hằng sốGiá trịNội dung
vbBinaryCompare0Phân biệt chữ in hoa và chữ thường, người dùng Nhật Bản: phân biệt bộ gõ zenkaku và hankaku, phân biệt hiragana và katakana
vbTextCompare
1
Không phân biệt chữ in hoa và chữ thường,...
Nếu giản lược, mặc định sẽ là vbBinaryCompare
Giá trị trả vềTrả về là mảng
Code mẫu:
Mã:
Sub FilterTest()
    Dim ar(5)
    Dim v
    
    ar(0) = "abc"
    ar(1) = "A"
    ar(2) = "  a  "
    ar(3) = "  b  "
    ar(4) = "  B  "
    ar(5) = "  B  "
    
    v = Filter(ar, "B", True, vbTextCompare)
    
    Dim s
    For Each s In v
        Debug.Print "[" & s & "]"
    Next
End Sub
Kết quả:
Mã:
[abc]
[  b  ]
[  B  ]
[  B  ]
Nguồn tham khảo và dịch:
 

PTHhn

Yêu THVBA như điếu đổ
Trước hết cho tôi cám ơn các viết bổ ích của các bạn trên diễn đàn. Tôi có mấy kiến thức sau chia sẻ cùng các bạn.
Nạp giá trị khởi tạo cho mảng:
Trên VBA, ta không thể khai báo một mảng toàn cục kiểu như thế này:
Mã:
Const c_mangArray() As String _
            = Array("XinchaoCacBan", "tuhocvba.net", "VN")
Thử biên dịch, các bạn sẽ thấy là có thông báo lỗi hiện ra.
Tôi thấy , admin tuhocvba đã có xử lý rất khéo.
Mã:
Public ngaylerr As Variant
Sub thietdinhngayle()
    ngaylerr = Array("1/1", "9/2")
End Sub
Mặc dù không khai báo dưới đạng hằng số rồi gán giá trị trực tiếp ngay khi khai báo, nhưng bằng cách gọi một thủ tục để thực hiện phép gán, đây cũng là một cách.

Nhân vấn đề này, tôi muốn thảo luận thêm.
Cách 1: Tôi sử dụng thuộc tính Get của Function để định nghĩa.
Mã:
Property Get c_mangArray(ByVal vIdx As Long) As String
    Dim wtemp As String
    '----------------
    Select Case vIdx
    Case 0: wtemp = "XinChaoCacBan"
    Case 1: wtemp = "tuhocvba.net"
    Case 2: wtemp = "VBA"
    End Select
    '----------------
    c_mangArray = wtemp
End Property

Sub Main()
    Dim i As Long
    For i = 0 To 2
        Debug.Print c_mangArray(i)
    Next i
End Sub
Kết quả:
Mã:
XinChaoCacBan
tuhocvba.net
VBA
Tôi nghĩ đây là cách đơn giản, tham số (vIdx) của Function là chỉ mục của mảng.
Tuy nhiên, tôi nghĩ nhiều người sẽ không thích cách này, vì mọi người muốn khai báo mảng chỉ trong một dòng. Càng ngắn gọn thì mọi người càng thích.
Điều này có thể tốt nếu mảng của bạn là giá trị không đổi, nhưng nếu sau này các bạn muốn áp dụng cho code khác, số lượng phần tử mảng nhiều hơn, thì cách viết ở trên không tốt. Do đó tôi thấy cách làm của admin tuhocvba là tốt.

Tôi muốn giới thiệu cách khác, chắc hẳn mọi người nhưng thường quên áp dụng trong các trường hợp cụ thể.
Cách 2: Sử dụng khai báo hằng số là chuỗi ký tự (việc này có thể dễ dàng thay đổi nội dung khi cần), rồi dùng Split để lấy mảng.
Mã:
Const c_mangArray As String = "XinChaoCacBan,tuhocvba.net,VBA"

Sub Main()
    Dim i As Long
    Dim wStrArray() As String

    wStrArray = Split(c_mangArray, ",")

    For i = 0 To UBound(wStrArray)
        Debug.Print wStrArray(i)
    Next i

End Sub
Kết quả:
Mã:
XinChaoCacBan
tuhocvba.net
VBA
 
Kiểm tra có phải là mảng hay không
Tương tự như kiểm tra số ta có Isnumeric, thì với mảng ta có IsArray.
Cụ thể:
Mã:
Sub vidu()
Dim MyArray(1 To 5) As Integer, YourArray    ' Declare array variables.
Dim MyCheck As Boolean
Dim a As Double
Dim b   'variant

YourArray = Array(1, 2, 3)    ' Use Array function.
MyCheck = IsArray(MyArray)    ' Returns True.
If MyCheck = True Then MsgBox "MyArray La mang" 'La mang
MyCheck = IsArray(YourArray)    ' Returns True.
If MyCheck = True Then MsgBox "YourArray La mang" 'La mang
a = 10
MyCheck = IsArray(a)
If MyCheck = True Then MsgBox "a La mang" 'Khong phai la mang
b = 100
MyCheck = IsArray(b)
If MyCheck = True Then MsgBox "b La mang" 'Khong phai la mang
End Sub
Nguồn tham khảo:
 

vbano1

SMod
Thành viên BQT
Xóa mảng:
Khi chạy chương trình, nếu mảng mà khai báo trong chương trình:
Mã:
Sub vidu()
Dim arr
arr = thisworkbook.sheets(1).Range....
End sub
Nếu mảng khai báo nội bộ như thế này, khi chạy hết thủ tục trên là mảng biến mất, bộ nhớ không có vấn đề gì.

Tuy nhiên khi khai báo public

Mã:
Public arr
Sub vidu()
arr = thisworkbook.sheets(1).Range....
End sub
Dù chạy hết thủ tục chính, thì mảng này vẫn còn mang giá trị, sẽ ảnh hưởng tới bộ nhớ.
Ở đây, mọi ngwowif thường dùng lệnh ReDim để giải phóng bộ nhớ mảng
Mã:
Public arr
Sub vidu()
arr = thisworkbook.sheets(1).Range....
....
Redim arr(1 to 1)
End sub
Tuy nhiên cách này vẫn chưa triệt để, sẽ chặt hơn nếu ta dùng lệnh Erase:
Mã:
Public arr
Sub vidu()
arr = thisworkbook.sheets(1).Range....
....
Erase arr()
End sub
Tham khảo:
 

vbano1

SMod
Thành viên BQT
Xác định số chiều của mảng:
Input : arr(1,2)
Output: 2

Input: arr(1)
Output:1
Mã:
Function sochieu_arr(ByVal arr As Variant) As Long
    Dim i As Long, Tempdata As Long
    
    On Error Resume Next
    i = 0
    Do While Err.Number = 0
        i = i + 1
        Tempdata = UBound(arr, i)
    Loop
    On Error GoTo 0
    sochieu_arr = i - 1
End Function
Sub test()
    Dim arr(1, 2)
    MsgBox sochieu_arr(arr)
End Sub
Nguồn tham khảo:
 

tuhocvba

Administrator
Thành viên BQT
Đảo chiều mảng:
Mã:
Function transposeArray(ByVal myarr As Variant) As Variant
    Dim myvar   As Variant 'Output
    Dim i   As Long, j  As Long
    
    ReDim myvar(LBound(myarr, 2) To UBound(myarr, 2), LBound(myarr, 1) To UBound(myarr, 1))
    
    For i = LBound(myarr, 2) To UBound(myarr, 2) Step 1
        For j = LBound(myarr, 1) To UBound(myarr, 1) Step 1
            myvar(i, j) = myarr(j, i)
        Next j
    
    Next i
    transposeArray = myvar
End Function
 

Euler

Administrator
Thành viên BQT
Xóa cột trong mảng hai chiều:
Mã:
'Nguon tham khao: https://vba-create.jp/vba-array2d-remove-column/
'Xoa cot chi dinh trong mang 2 chieu
Public Function Call_Array2D_RemoveColumn(arr As Variant, DelCol As Long)
   
   Dim rMin As Long: rMin = LBound(arr, 1)
   Dim rMax As Long: rMax = UBound(arr, 1)
   Dim cMin As Long: cMin = LBound(arr, 2)
   Dim cMax As Long: cMax = UBound(arr, 2)
   
   'So cot - 1
   Dim temp As Variant
   ReDim temp(rMin To rMax, cMin To cMax - 1)
                   
   Dim R As Long               'Row
   Dim C As Long               'Column
   Dim i As Long: i = cMin     'start from cMin
   
   'Neu den cot can xoa thi nhay i len i + 1
   For R = rMin To rMax
       For C = cMin To cMax - 1
           If C = DelCol Then
               i = i + 1
           End If
           temp(R, C) = arr(R, i)
           i = i + 1
       Next C
           i = cMin
   Next R

   Call_Array2D_RemoveColumn = temp
End Function
Public Sub test()
    Dim data() As Variant
    Dim i&, j&
    ReDim data(1 To 2, 1 To 3)
   
    data(1, 1) = 1
    data(2, 1) = 2
   
    data(1, 2) = "a"
    data(2, 2) = "b"
   
    data(1, 3) = "A"
    data(2, 3) = "B"
   
    '1 a A
    '2 b B
   
   
   
    'Xoa cot 1 cua mang data
    data = Call_Array2D_RemoveColumn(data, 1)
   
    '1 a
    '2 b
   
    For i = LBound(data, 1) To UBound(data, 1) Step 1
        For j = LBound(data, 2) To UBound(data, 2) Step 1
            Debug.Print data(i, j) & vbTab
        Next j
         Debug.Print Chr(10)
    Next i

End Sub
 

Euler

Administrator
Thành viên BQT
Xóa dòng trong mảng hai chiều:
Mã:
'Tham khao: https://vba-create.jp/vba-array2d-remove/
'Xoa dong chi dinh trong mang hai chieu
Public Function Call_Array2D_Remove(arr As Variant, DelRow As Long)
    
    Dim rMin As Long: rMin = LBound(arr, 1)
    Dim rMax As Long: rMax = UBound(arr, 1)
    Dim cMin As Long: cMin = LBound(arr, 2)
    Dim cMax As Long: cMax = UBound(arr, 2)
    
    
    Dim temp As Variant
    ReDim temp(rMin To rMax - 1, cMin To cMax)
                    
    Dim R As Long
    Dim C As Long
    Dim i As Long: i = rMin
    
    For R = rMin To rMax - 1
        'Neu toi dong can xoa thi tang i len i + 1
        If R = DelRow Then
            i = i + 1
        End If
        For C = cMin To cMax
            temp(R, C) = arr(i, C)
        Next C
        i = i + 1
    Next R
    
    Call_Array2D_Remove = temp
End Function
Public Sub test()
    Dim data() As Variant
    
    Dim i&, j&
    
    ReDim data(1 To 3, 1 To 2)
  
    data(1, 1) = 1
    data(2, 1) = 2
    data(3, 1) = 3
  
    data(1, 2) = "a"
    data(2, 2) = "b"
    data(3, 2) = "c"
  
  
    '1 a
    '2 b
    '3 c
    
    
    
    data = Call_Array2D_Remove(data, 2)
    
    '1 a
    '3 c
    
    For i = LBound(data, 1) To UBound(data, 1) Step 1
        For j = LBound(data, 2) To UBound(data, 2) Step 1
            Debug.Print data(i, j) & vbTab
        Next j
         Debug.Print Chr(10)
    Next i
End Sub
 
Top