Bài đăng này cung cấp một cái nhìn chuyên sâu về mảng VBA, một phần rất quan trọng của ngôn ngữ lập trình trong VBA Excel. Nó bao gồm mọi thứ bạn cần biết về VBA array trong Excel. Cùng Học Excel Online khám phá ngay nhé!
Xem nhanh
Trong phần đầu tiên, chúng ta sẽ xem xét VBA Array chính xác là gì. Bạn có thể không hiểu một số mã trong phần đầu tiên. Đừng lo lắng. Tôi sẽ chia nhỏ tất cả thành các thuật ngữ đơn giản trong các phần sau.
Phần đầu tiên bên dưới cung cấp hướng dẫn tham khảo nhanh để sử dụng VBA Array. Hãy quay lại nó bất cứ lúc nào bạn cần một lời nhắc nhanh về cách các phần của VBA Array hoạt động.
Phần còn lại của bài đăng cung cấp hướng dẫn đầy đủ nhất mà bạn sẽ tìm thấy về VBA array.
Các vòng lặp được sử dụng để đọc qua VBA Array:
VBA Collection – Tốt hơn khi bạn muốn tiếp tục chèn các mục vì nó tự động thay đổi kích thước.
VBA Array List – Danh sách này hư cấu hơn Collection.
VBA Dictionary – Cho phép lưu trữ một cặp Key \ Value. Rất hữu ích trong nhiều ứng dụng.
Công việc | Mảng tĩnh | Mảng động |
Khai báo | Dim arr(0 To 5) As Long | Dim arr() As Long
Dim arr As Variant |
Đặt kích thước | See Declare above | ReDim arr(0 To 5)As Variant |
Đặt kích thước (Số lượng mục) | See ArraySize function below. | See ArraySize function below. |
Tăng kích thước (Dữ liệu hiện có) | Dynamic Only | ReDim Preserve arr(0 To 6) |
Đặt giá trị | arr(1) = 22 | arr(1) = 22 |
Nhận giá trị | total = arr(1) | total = arr(1) |
Ví trí đầu tiên | LBound(arr) | LBound(arr) |
Vị trí cuối cùng | Ubound(arr) | Ubound(arr) |
Đọc tất cả mục(1D) | For i = LBound(arr) To UBound(arr)
Next i Or For i = LBound(arr,1) To UBound(arr,1) Next i |
For i = LBound(arr) To UBound(arr)
Next i Or For i = LBound(arr,1) To UBound(arr,1) Next i |
Đọc tất cả mục(2D) | For i = LBound(arr,1) To UBound(arr,1)
For j = LBound(arr,2) To UBound(arr,2) Next j Next i |
For i = LBound(arr,1) To UBound(arr,1)
For j = LBound(arr,2) To UBound(arr,2) Next j Next i |
Đọc tất cả mục | Dim item As Variant
For Each item In arr Next item |
Dim item As Variant
For Each item In arr Next item |
Chuyển qua Sub | Sub MySub(ByRef arr() As String) | Sub MySub(ByRef arr() As String) |
Trả về chức năng | Function GetArray() As Long()
Dim arr(0 To 5) As Long GetArray = arr End Function |
Function GetArray() As Long()
Dim arr() As Long GetArray = arr End Function |
Nhận chức năng | Dynamic only | Dim arr() As Long
Arr = GetArray() |
Xóa mảng | Erase arr
*Resets all values to default |
Erase arr
*Deletes array |
Chuỗi đến mảng | Dynamic only | Dim arr As Variant
arr = Split(“James:Earl:Jones”,”:”) |
Mảng đến chuỗi | Dim sName As String
sName = Join(arr, “:”) |
Dim sName As String
sName = Join(arr, “:”) |
Lấp đầy hết các giá trị | Chỉ động | Dim arr As Variant
arr = Array(“John”, “Hazel”, “Fred”) |
Vùng đến mảng | Chỉ động | Dim arr As Variant
arr = Range(“A1:D2”) |
Mảng đến vùng | Giống động | Dim arr As Variant
Range(“A5:D6”) = arr |
Vui lòng nhấp vào nút bên dưới để lấy mã nguồn được tài liệu đầy đủ cho bài viết này.
VBA Array là một kiểu biến. Nó được sử dụng để lưu trữ danh sách các dữ liệu cùng loại. Một ví dụ sẽ là lưu trữ danh sách các quốc gia hoặc danh sách tổng số hàng tuần.
Trong VBA, một biến bình thường chỉ có thể lưu trữ một giá trị tại một thời điểm.
Trong ví dụ sau, chúng ta sử dụng một biến để lưu trữ điểm của một học sinh:
Nếu chúng ta muốn lưu trữ điểm của một sinh viên khác thì chúng ta cần tạo một biến thứ hai.
Trong ví dụ sau, chúng ta có điểm của năm học sinh:
Chúng ta sẽ đọc những dấu này và ghi chúng vào Immediate Window.
Lưu ý: Hàm Debug.Print ghi giá trị vào Immediate Window. Để xem cửa sổ này, hãy chọn View->Immediate Window từ menu (Phím tắt là Ctrl + G)
Như bạn có thể thấy trong ví dụ sau, chúng tôi đang viết cùng một mã năm lần – một lần cho mỗi học sinh:
Vấn đề với việc sử dụng một biến cho mỗi học sinh là bạn cần thêm mã cho mỗi học sinh. Vì vậy, nếu bạn có một nghìn sinh viên trong ví dụ trên, bạn sẽ cần ba nghìn dòng mã!
May mắn thay, chúng ta có các mảng để làm cho cuộc sống của chúng ta dễ dàng hơn. Mảng cho phép chúng ta lưu trữ một danh sách các mục dữ liệu trong một cấu trúc.
Đoạn mã sau hiển thị ví dụ sinh viên ở trên bằng cách sử dụng một mảng:
Ưu điểm của mã này là nó sẽ hoạt động cho bất kỳ số lượng học sinh nào. Nếu chúng ta phải thay đổi mã này để đối phó với 1000 sinh viên, chúng ta chỉ cần thay đổi (1 To 5) thành (1 To 1000) trong khai báo. Trong ví dụ trước, chúng ta sẽ cần thêm khoảng năm nghìn dòng mã.
Hãy so sánh nhanh các biến và mảng. Đầu tiên chúng ta so sánh khai báo:
Tiếp theo, chúng ta so sánh việc gán một giá trị:
Cuối cùng, chúng ta xem xét việc viết các giá trị:
Như bạn thấy, cách sử dụng biến và mảng khá giống nhau.
Thực tế là các mảng sử dụng một chỉ mục (còn được gọi là chỉ số con) để truy cập từng mục là rất quan trọng. Nó có nghĩa là chúng ta có thể dễ dàng truy cập tất cả các mục trong một mảng bằng For Loop.
Bây giờ bạn đã có một số thông tin cơ bản về lý do tại sao các mảng lại hữu ích, hãy cùng tìm hiểu chúng từng bước.
Có hai loại VBA Array
Sự khác biệt giữa các loại này chủ yếu là ở cách chúng được tạo ra. Việc truy cập các giá trị trong cả hai kiểu mảng là hoàn toàn giống nhau. Trong các phần sau, chúng ta sẽ đề cập đến cả hai loại này.
Một mảng tĩnh được khai báo như sau:
Như bạn có thể thấy, độ dài được chỉ định khi bạn khai báo một mảng tĩnh. Vấn đề với điều này là bạn không bao giờ có thể chắc chắn trước độ dài bạn cần. Mỗi lần chạy Macro, bạn có thể có các yêu cầu về độ dài khác nhau.
Nếu bạn không sử dụng tất cả các vị trí của mảng thì tài nguyên đang bị lãng phí. Vì vậy, nếu bạn cần thêm vị trí, bạn có thể sử dụng ReDim nhưng về cơ bản đây là tạo một mảng tĩnh mới.
Mảng động không có vấn đề như vậy. Bạn không chỉ định độ dài khi khai báo. Do đó, bạn có thể phát triển và thu nhỏ theo yêu cầu:
Mảng động không được chỉ định cho đến khi bạn sử dụng câu lệnh ReDim. Ưu điểm là bạn có thể đợi cho đến khi bạn biết số lượng mục trước khi đặt độ dài mảng. Với một mảng tĩnh, bạn phải nêu trước độ dài.
Để đưa ra một ví dụ. Hãy tưởng tượng bạn đang đọc bảng tính điểm của học sinh. Với một mảng động, bạn có thể đếm số học sinh trên trang tính và đặt một mảng theo độ dài đó. Với mảng tĩnh, bạn phải đặt độ dài cho số học sinh lớn nhất có thể.
Để gán giá trị cho một mảng, bạn sử dụng số vị trí. Bạn chỉ định giá trị cho cả hai kiểu mảng theo cùng một cách:
Số của vị trí được gọi là chỉ số con hoặc chỉ mục. Dòng cuối cùng trong ví dụ sẽ cho lỗi “Chỉ số nằm ngoài vùng” vì không có vị trí 4 trong ví dụ mảng.
Không có hàm gốc để lấy số lượng item trong một mảng. Tôi đã tạo hàm ArrayLength bên dưới để trả về số lượng item trong bất kỳ mảng nào cho dù có bao nhiêu kích thước:
Bạn có thể sử dụng nó như sau:
Bạn có thể sử dụng hàm Array để điền vào một mảng với danh sách các mục. Bạn phải khai báo mảng là một biến kiểu. Đoạn mã sau đây cho bạn thấy cách sử dụng chức năng này.
Mảng được tạo bởi Array Function sẽ bắt đầu ở chỉ số 0 trừ khi bạn sử dụng Option Base 1 ở đầu mô-đun của mình. Sau đó, nó sẽ bắt đầu ở chỉ mục một. Trong lập trình, việc có dữ liệu thực của bạn trong mã được coi là hoạt động kém. Tuy nhiên, đôi khi nó hữu ích khi bạn cần kiểm tra nhanh một số mã.
Hàm Split được sử dụng để tách một chuỗi thành một mảng dựa trên dấu phân cách. Dấu phân cách là một ký tự như dấu phẩy hoặc dấu cách phân tách các mục.
Đoạn mã sau sẽ chia chuỗi thành một mảng gồm ba phần tử:
Hàm Split thường được sử dụng khi bạn đọc từ tệp được phân tách bằng dấu phẩy hoặc một nguồn khác cung cấp danh sách các mục được phân tách bằng cùng một ký tự.
Sử dụng vòng lặp với VBA array
Sử dụng Vòng lặp For cho phép truy cập nhanh vào tất cả các mục trong một mảng. Đây là lúc sức mạnh của việc sử dụng mảng trở nên rõ ràng. Chúng ta có thể đọc các mảng có mười giá trị hoặc mười nghìn giá trị bằng cách sử dụng cùng một vài dòng mã. Có hai chức năng trong VBA được gọi là LBound và UBound. Các hàm này trả về chỉ số con nhỏ nhất và lớn nhất trong một mảng. Trong một mảng arrMarks (0 to 3), LBound sẽ trả về 0 và UBound sẽ trả về 3.
Ví dụ sau đây gán các số ngẫu nhiên cho một mảng bằng vòng lặp. Sau đó, nó in ra những con số này bằng cách sử dụng vòng lặp thứ hai.
Các chức năng LBound và UBound rất hữu ích. Sử dụng chúng có nghĩa là các vòng lặp của chúng ta sẽ hoạt động chính xác với bất kỳ độ dài mảng nào. Lợi ích thực sự là nếu độ dài của mảng thay đổi, chúng ta không phải thay đổi mã để in các giá trị. Vòng lặp sẽ hoạt động cho một mảng có độ dài bất kỳ miễn là bạn sử dụng các hàm này.
Bạn có thể sử dụng vòng lặp For Each với các mảng. Điều quan trọng cần ghi nhớ là nó là Read-Only. Điều này có nghĩa là bạn không thể thay đổi giá trị trong mảng. Trong đoạn mã sau, giá trị của dấu thay đổi nhưng nó không thay đổi giá trị trong mảng.
Vòng lặp For Each là phù hợp để sử dụng để đọc một mảng. Đặc biệt là viết cho mảng Hai chiều sẽ gọn gàng hơn như chúng ta sẽ thấy.
Hàm Erase có thể được sử dụng trên mảng nhưng hoạt động khác nhau tùy thuộc vào kiểu mảng. Đối với một Mảng tĩnh, hàm Erase sẽ đặt lại tất cả các giá trị về mặc định. Nếu mảng được tạo thành từ các số nguyên dài (tức là loại Long) thì tất cả các giá trị được đặt thành 0. Nếu mảng là chuỗi thì tất cả các chuỗi được đặt thành “”, v.v.
Đối với một Mảng động, chức năng Erase DeAllocates bộ nhớ. Đó là, nó xóa mảng. Nếu muốn sử dụng lại bạn phải sử dụng ReDim để Phân bổ bộ nhớ.
Hãy xem một ví dụ cho mảng tĩnh. Ví dụ này giống với ví dụ ArrayLoops trong phần trước với một điểm khác biệt – chúng ta sử dụng Erase sau khi thiết lập các giá trị. Khi giá trị được in ra, tất cả chúng sẽ bằng 0:
Bây giờ chúng ta sẽ thử cùng một ví dụ với một động. Sau khi chúng ta sử dụng Erase tất cả các vị trí trong mảng đã bị xóa. Chúng ta cần sử dụng ReDim nếu chúng ta muốn sử dụng lại mảng.
Nếu chúng ta cố gắng truy cập các thành phần của mảng này, chúng ta sẽ gặp lỗi “Subscript out of Range“:
Nếu chúng ta sử dụng ReDim trên một mảng hiện có, thì mảng và nội dung của nó sẽ bị xóa.
Trong ví dụ sau, câu lệnh ReDim thứ hai sẽ tạo một mảng hoàn toàn mới. Mảng gốc và nội dung của nó sẽ bị xóa.
Nếu chúng ta muốn kéo dài độ dài của mảng mà không làm mất nội dung, chúng ta có thể sử dụng từ khóa Preserve.
Khi chúng ta sử dụng Redim Preserve mảng mới phải bắt đầu ở cùng một kích thước ban đầu, ví dụ: Chúng ta không thể Preserve từ (0 đến 2) thành (1 đến 3) hoặc (2 đến 10) vì chúng là các kích thước bắt đầu khác nhau.
Trong đoạn mã sau, chúng ta tạo một mảng bằng cách sử dụng ReDim và sau đó điền vào mảng với các loại trái cây.
Sau đó, chúng tôi sử dụng Preserve để mở rộng độ dài của mảng để không làm mất nội dung ban đầu:
Bạn có thể thấy hình bên dưới, rằng nội dung ban đầu của mảng đã được “Preserved“.
Lưu ý: Trong hầu hết các trường hợp, bạn không cần phải thay đổi kích thước một mảng như chúng ta đã làm trong phần này. Nếu bạn đang thay đổi kích thước một mảng nhiều lần thì bạn có thể muốn xem xét sử dụng Collection.
Preserve chỉ hoạt động với giới hạn trên của một mảng.
Ví dụ: nếu bạn có một mảng hai chiều, bạn chỉ có thể bảo toàn kích thước thứ hai như ví dụ này cho thấy:
Nếu chúng ta cố gắng sử dụng Preserve ở giới hạn thấp hơn, chúng ta sẽ nhận được lỗi “Subscript out of range“.
Trong đoạn mã sau, chúng ta sử dụng Preserve trên kích thước đầu tiên. Chạy mã này sẽ xuất hiện lỗi “Subscript out of range“:
Khi chúng ta đọc từ một vùng sang một mảng, nó sẽ tự động tạo ra một mảng hai chiều, ngay cả khi chúng ta chỉ có một cột.
Các quy tắc Preserve tương tự cũng được áp dụng. Chúng ta chỉ có thể sử dụng Preserve ở giới hạn trên như ví dụ này cho thấy:
Không có chức năng nào trong VBA để sắp xếp một mảng. Chúng ta có thể sắp xếp các ô trang tính nhưng điều này có thể chậm nếu có nhiều dữ liệu.
Hàm QuickSort bên dưới có thể được sử dụng để sắp xếp một mảng.
Bạn có thể sử dụng chức năng này như thế này:
Đôi khi bạn sẽ cần chuyển một mảng tới một quy tình. Bạn khai báo tham số bằng cách sử dụng dấu ngoặc đơn tương tự như cách bạn khai báo một mảng động.
Chuyển đến quy tình bằng ByRef có nghĩa là bạn đang chuyển một tham chiếu của mảng. Vì vậy, nếu bạn thay đổi mảng trong quy tình, nó sẽ được thay đổi khi bạn trả lại.
Lưu ý: Khi bạn sử dụng một mảng làm tham số, nó không thể sử dụng ByVal, nó phải sử dụng ByRef. Bạn có thể chuyển mảng bằng ByVal để biến tham số thành một biến thể.
Điều quan trọng là phải ghi nhớ những điều sau đây. Nếu bạn muốn thay đổi một mảng hiện có trong một quy trình thì bạn nên chuyển nó dưới dạng tham số bằng ByRef (xem phần cuối). Bạn không cần phải trả về mảng từ quy trình.
Lý do chính để trả về một mảng là khi bạn sử dụng quy trình để tạo một mảng mới. Trong trường hợp này, bạn gán mảng trả về cho một mảng trong trình gọi. Mảng này không thể được chỉ định. Nói cách khác, bạn phải sử dụng một mảng động chưa được chỉ định.
Các ví dụ sau đây cho thấy điều này:
Sử dụng VBA Array hai chiều
Các mảng mà chúng ta đang xem xét cho đến nay là mảng một chiều. Điều này có nghĩa là các mảng là một danh sách các mục.
Mảng hai chiều về bản chất là một danh sách các danh sách. Nếu bạn coi một hàng trong bảng tính là một chiều thì nhiều hơn một cột là hai chiều. Trên thực tế, một bảng tính tương đương với một mảng hai chiều.
Nó có hai chiều – hàng và cột. Một điều nhỏ cần lưu ý là Excel coi mảng một chiều như một hàng nếu bạn ghi nó vào bảng tính. Nói cách khác, mảng arr (1 đến 5) tương đương với arr (1 đến 1, 1 đến 5) khi ghi giá trị vào bảng tính.
Hình ảnh sau đây cho thấy hai nhóm dữ liệu. Đầu tiên là bố cục một chiều và thứ hai là hai chiều.
Để truy cập một item trong tập dữ liệu đầu tiên (1 chiều), tất cả những gì bạn cần làm là cung cấp hàng, ví dụ: 1,2, 3 hoặc 4.
Đối với tập dữ liệu thứ hai (hai chiều), bạn cần cung cấp cho hàng và cột. Vì vậy, bạn có thể nghĩ 1 chiều là nhiều cột và một hàng và hai chiều là nhiều hàng và nhiều cột.
Lưu ý: Có thể có nhiều hơn hai chiều trong một mảng. Nó hiếm khi được yêu cầu. Nếu bạn đang giải quyết một vấn đề bằng cách sử dụng mảng 3 chiều trở lên thì có lẽ có một cách tốt hơn để làm điều đó. Bạn khai báo một mảng hai chiều như sau:
Ví dụ sau tạo một giá trị ngẫu nhiên cho mỗi mục trong mảng và các giá trị này sẽ in ra Immediate Window:
Bạn có thể thấy rằng chúng ta sử dụng vòng lặp For thứ hai bên trong vòng lặp đầu tiên để truy cập tất cả các mục.
Đầu ra của ví dụ trông giống như sau:
Cách hoạt động của Macro này như sau:
Bạn có thể nhận thấy rằng LBound và UBound có tham số thứ hai với giá trị 2. Điều này xác định rằng nó là giới hạn trên hoặc giới hạn dưới của hai chiều. Đó là vị trí bắt đầu và kết thúc cho j. Giá trị mặc định 1 là lý do tại sao chúng ta không cần chỉ định nó cho vòng lặp i.
Sử dụng For Each dễ dàng hơn để sử dụng khi đọc từ một mảng.
Hãy lấy đoạn mã ở trên để viết ra mảng hai chiều:
Bây giờ chúng ta hãy viết lại nó bằng cách sử dụng vòng lặp For Each. Bạn có thể thấy chúng ta chỉ cần một vòng lặp và do đó, viết dễ dàng hơn nhiều:
Sử dụng vòng lặp For Each cung cấp cho chúng ta mảng theo một thứ tự duy nhất – từ LBound đến UBound. Hầu hết thời gian đây là tất cả những gì bạn cần.
Nếu bạn đã đọc bài viết trước của tôi về Cells and Ranges thì bạn sẽ biết rằng VBA có một cách cực kỳ hiệu quả để đọc từ Vùng giá trị ô sang Mảng và ngược lại
Mảng động được tạo trong ví dụ này sẽ là một mảng hai chiều. Như bạn thấy, chúng ta có thể đọc từ toàn bộ vùng giá trị ô sang mảng chỉ trong một dòng.
Ví dụ tiếp theo sẽ đọc dữ liệu sinh viên mẫu bên dưới từ C3: E6 của Sheet1 và in chúng ra Immediate Window:
Như bạn có thể thấy chiều đầu tiên (được truy cập bằng cách sử dụng i) của mảng là một hàng và thứ hai là một cột. Để chứng minh điều này, hãy xem giá trị 44 trong E4 của dữ liệu mẫu. Giá trị này nằm trong hàng 2 cột 3 trong dữ liệu của chúng ta. Bạn có thể thấy rằng 44 được lưu trữ trong mảng tại StudentMarks (2,3).
Cách làm cho Macro của bạn chạy với tốc độ cực nhanh
Nếu macro của bạn đang chạy rất chậm thì bạn có thể thấy phần này rất hữu ích. Đặc biệt nếu bạn đang xử lý một lượng lớn dữ liệu. Sau đây là một bí mật rất được giữ kín trong VBA
Trong phần trước, bạn đã thấy cách chúng ta có thể dễ dàng đọc từ một nhóm ô sang một mảng và ngược lại. Nếu chúng ta đang cập nhật nhiều giá trị thì chúng ta có thể làm như sau:
Ví dụ: mã sau sẽ nhanh hơn nhiều so với mã bên dưới:
Việc gán từ một tập hợp các ô khác cũng nhanh hơn nhiều so với việc sử dụng Copy và Paste:
Nhận xét sau đây là của hai độc giả đã sử dụng mảng để tăng tốc macro của họ
“Một vài dự án của tôi đã đi từ gần như không thể và lâu thành gần như quá dễ dàng và giảm thời gian chạy từ 10: 1.” – Dane
“Một báo cáo tôi đã thực hiện mất gần 3 giờ để chạy khi truy cập trực tiếp vào các ô – 5 phút với mảng” – Jim
Sau đây là tóm tắt những điểm chính của bài đăng này:
Chắc hẳn bạn đã nắm được những điều cơ bản trong mảng VBA rồi. Hãy nhớ VBA Array rất quan trọng nên phải chú trọng nắm vững nhé. Học Excel Online chúc bạn nhanh chóng thuần thục VBA Excel nhé!