HƯỚNG DẪN CÁCH SỬ DỤNG TỪ ĐIỂN VBA TRONG EXCEL ĐẦY ĐỦ

“Tác phẩm văn học vĩ đại nhất chẳng qua là một cuốn từ điển không sắp xếp nào cả.” – Jean Cocteau. Bài viết dưới đây của Siêu Marketing sẽ giới thiệu cho các bạn cách sử dụng từ điển VBA trong Excel tỉ mỉ và cặn kẽ.

Hướng dẫn cơ bản về từ điển VBA

Chức năng Params
Thêm tham chiếu “Microsoft Scripting Runtime” (Cài đặt từ Tools -> References trong menu VB)
Khai báo (ràng buộc sớm) Dim dict As Scripting.Dictionary
Tạo mới (Ràng buộc sớm) Set dict = New Scripting.Dictionary
Cách khai báo (Ràng buộc muộn) Dim dict As Object
Tạo mới (Ràng buộc muộn) Set dict = CreateObject(“Scripting.Dictionary”)
Thêm phần tử mới (Key chưa tồn tại) dict.Add Key, Value

Ví dụ: dict.Add “Apples”, 50

Thay đổi giá trị của Key hoặc tự động thêm nếu không có Key dict(Key) = Value

Ví dụ: dict(“Oranges”) = 60

Lấy giá trị từ từ điển qua Key Value= dict(Key)

Ví dụ: appleCount = dict(“Apples”)

Xác định xem Key có trong từ điển hay không dict.Exists(Key)

Ví dụ: If dict.Exists(“Apples”) Then

Xoá một phần tử dict.Remove Key

Ví dụ: dict.Remove “Apples”

Xoá toàn bộ phần tử dict.RemoveAll
Duyệt qua các phần tử (với mỗi lần lặp) Dim key As Variant

For Each key In dict.Keys

    Debug.Print key, dict(key)

Next key

Duyệt qua các phần tử (vòng lặp – chỉ có trong rang buộc sớm) Dim i As Long

For i = 0 To dict.Count – 1

   Debug.Print dict.Keys(i),      dict.Items(i)

Next i

Duyệt qua các phần tử (vòng lặp – dùng cho cả rang buộc sớm và muộn) Dim i As Long

For i = 0 To dict.Count – 1

Debug.Print dict.Keys()(i), dict.Items()(i)

Next i

Tính số lượng phần tử dict.Count
Cài đặt phân biệt chữ hoa và chữ thường cho Key (từ điển cần trống) dict.CompareMode = vbBinaryCompare
Cài đặt không phân biệt chữ hoa và chữ thường cho Key (từ điển cần trống) dict.CompareMode = vbTextCompare

 

VBA từ điển là gì?

Trong VBA, chúng ta thường dùng Mảng và Collection để quản lý nhóm các giá trị. Những cấu trúc này giúp chúng ta lưu danh sách tên của khách hàng, điểm số của học sinh hay một loạt giá trị từ các ô không liền kề nhau.

Từ điển VBA khá giống với Bộ sưu tập. Bằng việc sử dụng chúng, ta có thể đặt tên cho mỗi phần tử khi chúng ta thêm chúng vào. Hãy tưởng tượng khi chúng ta cần ghi nhớ số lượng của các loại trái cây khác nhau.

Chúng ta có thể áp dụng Collection và Dictionary như sau:

' Thêm vào Từ điển
dict.Add Key:="Apple", Item:=5

' Thêm vào Bộ sưu tập
coll.Add Item:=5, Key:="Apple"
1-TỪ ĐIỂN VBA TRONG EXCEL
1-TỪ ĐIỂN VBA TRONG EXCEL
 

Ở cả hai trường hợp, giá trị 5 được lưu giữ và “Apple” là tên gọi cho nó. Giờ đây, giá trị của Apple có thể được lấy ra bằng cách sau từ cả hai kiểu:

' Lấy giá trị từ Dictionary
Total = dict("Apple")

' Lấy giá trị từ Collection
Total = coll("Apple")

Mọi việc dường như ổn thỏa. Nhưng, Collection có hai điểm yếu chính:

  1. Không thể kiểm tra liệu Key đã được tạo hay chưa.
  2. Không thể thay đổi giá trị của một phần tử đã có.

Trường hợp thứ nhất xảy ra khá thường:  Check Collection Key exists. Trường hợp thứ hai lại khó khăn hơn.

Ngược lại, Từ điển VBA không gặp các vấn đề kể trên. Chúng cho phép kiểm tra sự tồn tại của Key và cả việc thay đổi giá trị của Item và Key.

Ví dụ, chúng ta có thể dùng đoạn mã sau để xác minh liệu có tồn tại mặt hàng có tên là Apple hay không.

If dict.Exists("Apple") Then 
    dict("Apple") = 78

Sự khác biệt này dù đơn giản nhưng làm cho Từ điển trở nên vô cùng hữu dụng cho những công việc cụ thể, nhất là khi cần truy xuất giá trị của một phần tử.

Ứng dụng thực tế của Từ điển

Để hiểu sâu hơn về Từ điển, hãy tưởng tượng như sau. Một từ điển ngoài đời thực bao gồm danh sách các Key và các Item, trong đó Key là các từ và Item là các định nghĩa của chúng.

Bạn muốn tra cứu định nghĩa của một từ, bạn chỉ cần tìm ngay đến từ đó mà không cần phải đọc lần lượt tất cả các Item trong từ điển.

Ví dụ thứ hai từ thực tế là sử dụng danh bạ điện thoại (bạn có nhớ danh bạ không?). Tên, địa chỉ chính là Key và số điện thoại là Item. Ta dùng tên và địa chỉ để tìm kiếm số điện thoại nhanh chóng.

Trong Excel, hàm VLookup cũng làm việc tương tự như một Từ điển, cho phép bạn tìm kiếm một phần tử dựa trên một giá trị đặc biệt.

Ví dụ đơn giản về việc sử dụng Từ điển VBA

Đoạn code dưới đây mang đến một minh họa đơn giản về việc ứng dụng Từ điển như sau:

  1. Đưa vào ba loại quả và một giá trị tương ứng cho từng loại trong Từ điển.
  2. Yêu cầu người dùng nhập vào tên một loại quả.
  3. Code sẽ kiểm tra quả đó có nằm trong Từ điển hay không.
  4. Nếu có, tên và giá trị của quả sẽ được hiển thị ra.
  5. Nếu không, thông báo cho người dùng biết rằng không tìm thấy loại quả đó.
' https://excelmacromastery.com/
Sub CheckFruit()

    ' Chọn Tools->References từ menu Visual Basic.
    ' Đánh dấu vào "Microsoft Scripting Runtime" trong danh sách.
    Dim dict As New Scripting.Dictionary
    
    ' Thêm trái cây vào Từ điển
    dict.Add key:="Apple", Item:=51
    dict.Add key:="Peach", Item:=34
    dict.Add key:="Plum", Item:=43

    Dim sFruit As String
    ' Hỏi người dùng nhập tên trái cây
    sFruit = InputBox("Please enter the name of a fruit")

    If dict.Exists(sFruit) Then
        MsgBox sFruit & " exists and has value " & dict(sFruit)
    Else
        MsgBox sFruit & " does not exist."
    End If
    
    Set dict = Nothing
    
End Sub

Ví dụ trên giản đơn nhưng diễn giải rõ ràng về tính hữu ích của Từ điển. Chúng ta sẽ xem xét một ví dụ thực tế hơn trong bài học tiếp theo.

Tại đoạn cuối của bài, hãy cùng nắm vững những nguyên lý cơ bản khi dùng Từ điển.

Khởi tạo từ điển

Bạn cần phải đính kèm thư viện trước khi sử dụng Từ điển.

  1. Nhấn chọn Tools -> References từ menu của Visual Basic.
  2. Tìm kiếm và chọn Microsoft Scripting Runtime bằng cách đánh dấu kiểm vào ô bên cạnh nó.

Cách khai báo một từ điển như sau:

Dim dict As New Scripting.Dictionary

Hoặc

Dim dict As Scripting.Dictionary
Set dict = New Scripting.Dictionary

Phương thức tạo từ điển này gọi là “Early Binding”. Còn có một phương thức khác là “Late Binding”, chúng ta sẽ xem xét ngay sau đây.

So sánh Early Binding và Late Binding

Khởi tạo từ điển sử dụng Late Binding thì dùng đoạn code sau, không cần đến việc thêm tham chiếu.

Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")

“Early binding” có nghĩa là chúng ta định nghĩa trước đối tượng đang được dùng. Còn với “Late binding” thì quá trình đó diễn ra khi chương trình đang chạy. Để giải thích một cách đơn giản, điểm khác biệt chủ yếu là:

  1. “Early binding” yêu cầu thêm thư viện từ Tools References, trong khi “Late binding” thì không.
  2. “Early binding” có thể truy cập vào *Intellisense, công cụ hỗ trợ gợi ý khi lập trình, “Late binding” thì không thể.
  3. “Early binding” đôi khi cần phải thêm thủ công tham chiếu “Microsoft Scripting Runtime” cho một số máy người dùng.

(*Intellisense là công cụ hỗ trợ hiển thị các phương thức và thuộc tính khả dụng của một đối tượng trong khi bạn đang nhập mã.)

Mặc dù Microsoft khuyến cáo nên sử dụng “early binding” trong phần lớn trường hợp, nhưng tôi lại có suy nghĩ khác. Theo quy tắc chung, bạn nên sử dụng “early binding” trong quá trình phát triển mã nguồn để có thể sử dụng Intellisense. Trong khi đó, “late binding” nên được dùng khi cung cấp mã nguồn cho người dùng khác để tránh xung đột giữa các thư viện.

Cách thêm Item vào từ điển

Chức năng Tham số Mẫu ví dụ
Add Key, Item dict.Add “Apples”, 50

Bạn có thể thêm Item vào từ điển sử dụng chức năng Add. Ngoài ra, ta cũng có thể thêm các Item để gán một giá trị, chúng ta sẽ khám phá điều này ở phần sau.

Xem xét chức năng Add, hàm này đòi hỏi hai tham số: Key và Item, và cả hai không thể thiếu.

Trong ví dụ đầu tiên giới thiệu việc thêm mục ở trên, chúng ta đã sử dụng tên tham số. Dù điều này không bắt buộc nhưng lại rất có ích cho những ai mới bắt đầu.

dict.Add Key:="Orange", Item:=45
dict.Add "Apple", 66
dict.Add "12/12/2015", "John"
dict.Add 1, 45.56

Key có thể được định dạng bởi bất kỳ kiểu dữ liệu nào. Item cũng tương tự, có thể là bất kì kiểu dữ liệu nào, đối tượng (Object), mảng (Array), tập hợp (Collection) hoặc ngay cả một từ điển khác (Dictionary). Vì vậy có khả năng tạo ra một từ điển chứa từ điển, mảng và tập hợp. Tuy nhiên, thông thường nó sẽ là một giá trị cụ thể (ngày tháng, số hay văn bản).

Nếu bạn thêm một Key đã có trong Từ điển, lỗi sẽ xảy ra.

“`html
2-TỪ ĐIỂN VBA TRONG EXCEL
2-TỪ ĐIỂN VBA TRONG EXCEL

Sau đây là mã lệnh gây lỗi

dict.Add Key:="Orange", Item:=45

' Dòng này sẽ báo lỗi do khóa đã tồn tại
dict.Add Key:="Orange", Item:=75

Xác định một giá trị

Công việc Định dạng Thí dụ
Đặt giá trị cho một key bên trong Dictionary Dictionary(Key) = Item dict(“Oranges”) = 60

Với đoạn mã dưới đây, ta có thể thay đổi giá trị của một khóa

dict("Orange") = 75

Khi gán giá trị cho Key theo phương pháp này, có một lợi ích đặc biệt. Key không hiện hữu sẽ được tự động thêm vào từ điển cùng với Item. Phương pháp này rất có ích nếu bạn đang quản lý danh sách các mục và mong muốn chỉ lưu lại mục nhập mới nhất cho mỗi mục.

' Thêm Orange vào từ điển 
dict("Orange") = 45 

' Cập nhật giá trị của Orange thành 100
dict("Orange") = 100

Hãy nhớ rằng bạn có thể tải về mã VBA đã dùng trong bài viết này ở phần đầu hoặc phần cuối của bài.

Kiểm soát sự tồn tại của Key

Tính năng Tham số Dẫn chứng
Exists Key If dict.Exists(“Apples”) Then

Ta dùng hàm Exists như một cách để xác minh liệu Key có mặt trong từ điển hay không.

' Kiểm tra khóa 'Orange' trong từ điển
If dict.Exists("Orange") Then
    MsgBox "Số lượng cam là " & dict("Orange") 
Else
    MsgBox "Không có mục nào cho Cam trong từ điển."
End If

Ghi lại nhiều giá trị trong một Key

Xét dữ liệu mẫu sau. Chúng ta cần lưu Amount và Item liên quan đến từng Customer ID.

Một từ điển chỉ chứa một giá trị cho mỗi khóa, vậy làm thế nào chúng ta có thể lưu trữ nhiều hơn?

Ta có thể dùng mảng hay collection làm giá trị, nhưng không cần thiết phức tạp vậy. Giải pháp tối ưu là dùng Class Module.

Đoạn code dưới đây mô tả cách thực hiện quá trình này.

' clsCustomer Class Module Code
Public CustomerID As String
Public Amount As Long
Public Items As Long
' Tạo một đối tượng clsCustomer mới
Set oCust = New clsCustomer

' Thiết lập các giá trị
oCust.CustomerID = rg.Cells(i, 1).Value
oCust.Amount = rg.Cells(i, 2).Value
oCust.Items = rg.Cells(i, 3).Value

' Thêm đối tượng clsCustomer mới vào từ điển
dict.Add oCust.CustomerID, oCust

Bạn có thể thấy, nhờ có Class Module, chúng ta tự do lưu giữ bất kỳ số lượng trường nào mong muốn. Phần ví dụ thứ 2 và thứ 3 cuối bài viết sẽ trình bày cách dùng Class Module với Từ điển.

Những chức năng tiện ích khác

Chức năng Tham số Ví dụ
Count N/A dict.Count
Remove Key dict.Remove “Apples”
Xóa tất cả N/A dict.RemoveAll

Các chức năng liệt kê trong bảng phía trên bao gồm:

  1. Count – Đưa ra số lượng các mục có trong Từ điển.
  2. Remove – Gỡ bỏ Key chỉ định khỏi từ điển.
  3. “`

    1. RemoveAll – phương thức để loại bỏ tất cả các phần tử khỏi Đối tượng Từ điển

    Dưới đây là một thí dụ minh họa cách thức sử dụng những function được trình bày trên.

    ' https://excelmacromastery.com/
    Sub AddRemoveCount()
    
        Dim dict As New Scripting.Dictionary
    
        ' Thêm một số phần tử
        dict.Add "Orange", 55
        dict.Add "Peach", 55
        dict.Add "Plum", 55
        Debug.Print "Số lượng mục là " & dict.Count
        
        ' Xóa một phần tử
        dict.Remove "Orange"
        Debug.Print "Số lượng mục là " & dict.Count
        
        ' Xóa tổng thể các phần tử
        dict.RemoveAll
        Debug.Print "Số lượng mục là " & dict.Count
    
    End Sub

    Đừng quên rằng có thể download tất cả mã nguồn ví dụ từ bài viết. Cần đi đến mục tải về phía trên cùng của trang.

    Key và việc so sánh hoa/thường

    Một vài hàm chuỗi trong VBA hỗ trợ vbCompareMethod, được dùng cho việc so sánh chuỗi. Nó cung cấp cách thức xác định liệu sự việc hoa thường có ảnh hưởng không.

    Đối tượng Từ điển cũng sử dụng cơ chế tương tự. Thuộc tính CompareMode trong từ điển xác định quan trọng của trường hợp các key. Cấu hình có thể là:

    vbTextCompare: Coi chữ hoa và thường giống hệt nhau.

    vbBinaryCompare: Coi chữ hoa và thường là khác biệt và là cài đặt mặc định.

    Chúng ta có thể áp dụng những cấu hình này khi làm việc với Từ điển để xác định tầm quan trọng của việc phân biệt hoa thường trong các key.

    ' https://excelmacromastery.com/
    Sub CaseMatters()
        
        Dim dict As New Scripting.Dictionary
        dict.CompareMode = vbBinaryCompare
        dict.Add "Orange", 1
        
        ' In ra False vì nó phân biệt "Orange" với "ORANGE" 
        Debug.Print dict.Exists("ORANGE")    
        
        Set dict = Nothing
    
    End Sub

    Khi sử dụng vbTextCompare, sự phân biệt hoa thường không được coi trọng:

    ' https://excelmacromastery.com/
    Sub CaseMattersNot()
        
        Dim dict As New Scripting.Dictionary
        dict.CompareMode = vbTextCompare
        dict.Add "Orange", 1
        
        ' In ra True vì "Orange" và "ORANGE" được xem là như nhau
        Debug.Print dict.Exists("ORANGE")    
        
        Set dict = Nothing
    
    End Sub

    Lưu ý: Thuộc tính CompareMode chỉ sử dụng được khi Từ điển không chứa phần tử nào, nếu không sẽ phát sinh lỗi “Invalid procedure call or argument”.

    Điểm lưu ý

    vbBinaryCompare (phan biệt hoa thường) được áp dụng làm mặc định và có thể gây ra lỗi không đáng có. Chẳng hạn, xét dữ liệu sau từ ô A1 đến B2:

    Orange, 5

    orange, 12

    Đoạn mã dưới đây sẽ tạo ra hai key khác nhau cho “Orange” và “orange”, mà chỉ có sự khác biệt về chữ cái đầu trong trường hợp của chúng.

    ' https://excelmacromastery.com/
    Sub DiffCase()
    
        Dim dict As New Scripting.Dictionary
        
        dict.Add Key:=(Range("A1")), Item:=Range("B1")
        dict.Add Key:=(Range("A2")), Item:=Range("B2")
    
    End Sub

    Khi dùng vbTextCompare cho dữ liệu tương tự, phát sinh lỗi trong quá trình thêm key thứ hai vì nó xem “Orange” và “orange” như nhau.

    ' https://excelmacromastery.com/
    Sub UseTextcompare()
    
        Dim dict As New Scripting.Dictionary
        dict.CompareMode = vbTextCompare
        
        dict.Add Key:=(Range("A1")), Item:=Range("B1")
        ' Dòng này sẽ gây lỗi do bạn thêm cùng một key lần nữa
        dict.Add Key:=(Range("A2")), Item:=Range("B2")
    
    End Sub

    Nếu phương pháp gán được sử dụng, nó sẽ bỏ qua CompareMode . Vì thế, đoạn mã sau vẫn thêm hai key dù rằng CompareMode được thiết lập là vbTextCompare.

    ' https://excelmacromastery.com/
    Sub GanGiaTri()
        
        Dim tuDien As New Scripting.Dictionary
        tuDien.CompareMode = vbTextCompare
        
        ' Thêm hai phần tử
        tuDien(Range("A1")) = Range("B1")
        tuDien(Range("A2")) = Range("B2")
        
        ' In ra số 2
        Debug.Print tuDien.Count
        
    End Sub

    Duyệt qua từ điển

    Ta có thể lướt qua từng phần tử của Từ điển. Cú pháp dùng để duyệt key thông qua vòng lặp For Each. Tiếp đó, dùng key để truy xuất giá trị tương ứng.

    Dim k As Variant
    For Each k In tuDien.Keys
        ' In ra key và giá trị tương ứng
        Debug.Print k, tuDien(k)
    Next

    Lặp qua các key cũng có thể thực hiện mặc dù tính năng này chỉ sử dụng được với Early Binding (Cập nhật tháng 2 năm 2020: Tính năng này giờ đây có thể sử dụng với cả Office 365):

    Dim i As Long
    For i = 0 To tuDien.Count - 1
        Debug.Print tuDien.Keys(i), tuDien.Items(i)
    Next i

    Cách làm này khả dụng cho cả Early và Late binding:

    Dim i As Long
    For i = 0 To tuDien.Count - 1
       Debug.Print tuDien.Keys()(i), tuDien.Items()(i)
    Next i

    Xếp thứ tự từ điển

    Đôi khi bạn muốn sắp xếp Từ điển theo key hoặc giá trị.

    Vì Từ điển không hỗ trợ tính năng sắp xếp nên bạn cần tự thực hiện. Tôi đã tạo hai hàm xếp thứ tự – một sắp xếp theo key và một sắp xếp theo giá trị.

    Xếp thứ tự theo key

    Để xếp thứ tự Từ điển theo key, bạn có thể dùng hàm SortDictionaryByKey dưới đây:

    CôngCụ SortDictionaryByKey(tuDien AsĐốitượng _

                      , Tùy chọn sortorder As XlSortOrder = xlAscending) AsĐốitượng

        Dim danhSachMang AsĐốitượng

        Set danhSachMang = CreateObject(“System.Collections.ArrayList”)

        ‘ Đưa key vào danh sách ArrayList

        Dim key AsBiến, tapHop AsNew Collection

        ForEach key Trong tuDien

            danhSachMang.Add key

        Tiếp key

        ‘ Sắp xếp key

        danhSachMang.Sort

        ‘ Đảo ngược nếu thứ tự là giảm dần

        Nếu sortorder = xlDescending Thì

            danhSachMang.Reverse

        KếtThúcNếu

        ‘ Tạo từ điển mới

        Dim tuDienMoi AsĐốitượng

        Set tuDienMoi = CreateObject(“Scripting.Dictionary”)

        ‘ Duyệt qua key đã xếp và thêm vào từ điển mới

        ForEach key Trong danhSachMang

            tuDienMoi.Add key, tuDien(key)

        Tiếp key

        ‘ Dọn dẹp

        Set danhSachMang = Khôngcòn

        Set tuDien = Khôngcòn

        ‘ Trả về từ điển mới

        Set SortDictionaryByKey = tuDienMoi

    KếtThúcHàm

    Đoạn mã sau minh hoạ cách sử dụng hàm SortDictionaryByKey

    ' https://excelmacromastery.com/
    Sub KiểmTraSắpXếpTheoKey()
    
        Dim tuDien As Object
        Set tuDien = CreateObject("Scripting.Dictionary")
        
        tuDien.Add "Mận", 99
        tuDien.Add "Táo", 987
        tuDien.Add
    Sub Main()
    
        ' Khởi tạo từ điển mới
        Dim dict As Object
        Set dict = CreateObject("Scripting.Dictionary")
        
        ' Thêm các cặp khóa và giá trị vào từ điển
        dict.Add "Lê", 234
        dict.Add "Chuối", 560
        dict.Add "Cam", 34
        
        ' In từ điển ra console
        InTuDien "Ban đầu", dict
        
        ' Sắp xếp tăng dần theo khóa
        Set dict = SapXepTuDienTheoKhoa(dict)
        InTuDien "Khóa Tăng dần", dict
        
        ' Sắp xếp giảm dần theo khóa
        Set dict = SapXepTuDienTheoKhoa(dict, xlDescending)
        InTuDien "Khóa Giảm dần", dict
        
    End Sub
    
    Public Sub InTuDien(ByVal sText As String, dict As Object)
        
        Debug.Print vbCrLf & sText & vbCrLf & String(Len(sText), "=")
        
        ' Duyệt qua mỗi khóa trong từ điển và in giá trị tương ứng
        Dim khoa As Variant
        For Each khoa In dict.keys
            Debug.Print khoa, dict(khoa)
        Next
    End Sub

    Sắp xếp theo giá trị

    Khi muốn sắp xếp một từ điển dựa trên giá trị của nó, bạn có thể áp dụng hàm SapXepTuDienTheoGiaTri dưới đây.

    ‘ https://excelmacromastery.com/

    PublicFunction SapXepTuDienTheoGiaTri(dict AsObject _

                        , Tuỳ chọn sapXep As XlSortOrder = xlAscending) AsObject

        On Error GoTo ql

        Dim danhSachMang AsObject

        Set danhSachMang = CreateObject(“System.Collections.ArrayList”)

        Dim dictTam AsObject

        Set dictTam = CreateObject(“Scripting.Dictionary”)

        ‘ Đưa giá trị vào danhSachMang và sắp xếp

        ‘ Lưu trữ giá trị vào dictTam với khóa của chúng trong một tập hợp

        Dim khoa AsVariant, giaTri AsVariant, tapHop As Collection

        ForEach khoa In dict

            giaTri = dict(khoa)

            ‘ Nếu giá trị chưa tồn tại trong dictTam thì thêm vào

            If dictTam.exists(giaTri) = FalseThen

                ‘ Tạo tập hợp để giữ các khóa

                ‘ – cần thiết đối với giá trị trùng lặp

                Set tapHop = New Collection

                dictTam.Add giaTri, tapHop

                ‘ Thêm giá trị vào

                danhSachMang.Add giaTri

            EndIf

            ‘ Thêm khóa hiện tại vào tập hợp

            dictTam(giaTri).Add khoa

        Next khoa

        ‘ Sắp xếp giá trị

        danhSachMang.Sort

        ‘ Đảo ngược nếu là sắp xếp giảm dần

        If sapXep = xlDescending Then

            danhSachMang.Reverse

        EndIf

        dict.RemoveAll

        ‘ Đọc và thêm các giá trị cùng với khóa tương ứng từ dictTam

        ‘ vào từ điển

        Dim phanTu AsVariant

        ForEach giaTri In danhSachMang

            Set tapHop = dictTam(giaTri)

            ForEach phanTu In tapHop

                dict.Add phanTu, giaTri

            Next phanTu

        Next giaTri

        Set danhSachMang = Nothing

        ‘ Trả lại từ điển mới

        Thiết lập SortDictionaryByValue = dict

    Hoàn thành:

        Thoát Hàm

    eh:

        Nếu Err.Number là 450 thì

            Err.Sinh lỗi vbObjectError + 100, “SortDictionaryByValue” _

                    , “Không thể sắp xếp từ điển nếu giá trị là đối tượng”

        Kết thúc Nếu

    Kết thúc Hàm

    Dưới đây là hướng dẫn sử dụng hàm SortDictionaryByValue

    Giải quyết vấn đề từ điển

    Mục này nêu lên những lỗi thường gặp khi dùng Từ điển.

    Bỏ qua tham chiếu

    Vấn đề: Bạn gặp thông báo lỗi “User-defined type not defined” 

    Thường gặp khi bạn tạo Từ điển nhưng lại quên thêm tham chiếu vào.

    Dim dict As New Scripting.Dictionary

    Cách giải quyết: Vào Tools->Reference trong menu Visual Basic. Chọn “Microsoft Scripting Runtime”.

    Tham khảo phần: Tạo từ điển.

    Exists không hiệu quả

    Vấn đề: Bạn thêm key vào Từ điển nhưng phát hiện hàm Exists lại báo false

    Thường là do Phân biệt chữ hoa chữ thường (xem phần trên).  

    dict.Add "Apple", 4
    
    If dict.Exists("apple") Then
        MsgBox "Exists"
    Else
        MsgBox "Does not Exist"
    End If

    Code trên đã thêm key “Apple”.

    Khi kiểm tra với “apple”, nó trả về false vì cách tính chữ viết:

    Bạn có thể thay đổi CompareMode sang vbTextCompare để bỏ qua chuyện đó:

    Dim dict As New Scripting.Dictionary
    dict.CompareMode = vbTextCompare

    Cách giải quyết: Chỉnh CompareMode thành vbTextCompare để không phân biệt hoa thường hoặc chắc chắn rằng dữ liệu nhập đúng cách.

    Xem phần: Key và Phân biệt chữ hoa chữ thường

    Lỗi Object

    Vấn đề: Xuất hiện thông báo lỗi “Object variable or With block variable not set” khi sử dụng Từ điển.

    Thường xảy ra nếu bạn quên dùng New trước Từ điển. Mã sau đây sẽ gây lỗi:

    Dim dict As Scripting.Dictionary
    ' Dòng này sẽ gây lỗi "Object variable..."
    dict.Add "Apple", 4

    Cách giải quyết: Khởi tạo Từ điển với New

    Dim dict As New Scripting.Dictionary

    Hoặc

    Dim dict As Scripting.Dictionary
    Set dict = New Scripting.Dictionary

    Tham khảo phần: Tạo từ điển

    Các mẹo giúp debug từ điển

    Khi bạn tìm hiểu vấn đề trong Từ điển, việc kiểm tra nội dung rất hữu ích.

    Dùng code sau để in ra mỗi Key và Item tới cửa sổ Immediate (Ctrl + G).

    ' https://excelmacromastery.com/
    Sub PrintContents(dict As Scripting.Dictionary)
        
        Dim k As Variant
        For Each k In dict.Keys
            ' Print key and value
            Debug.Print k, dict(k)
        Next
    
    End Sub

    Bạn có thể sử dụng nó như sau:

    Dim dict As Scripting.Dictionary
    Set dict = New Scripting.Dictionary
    
    ' Thêm các mục vào Từ điển
    
    ' In nội dung của Từ điển ra cửa sổ Immediate
    PrintContents dict

    Khi kiểm tra code, bạn cũng có thể thêm dict.Count vào cửa sổ Watch để xem số lượng mục trong Từ điển. Click chuột phải trong cửa sổ code và chọn Thêm Điểm Theo Dõi. Điền dict.Count vào ô văn bản và nhấn Ok.

    BạnBạn có thể thao tác với Từ điển ngay trên cửa sổ Watch bằng cách thêm Dict vào đó. Khi thực hiện thao tác nhấp vào nút cộng, bạn có thể quan sát nội dung của Từ điển, tuy nhiên nó chỉ cho thấy key mà không hiển thị item kèm theo.

    Xin lưu ý: Chế độ xem Watch chỉ khả dụng khi code đang thực thi.

    Đừng quên rằng, bạn có cơ hội tải về toàn bộ mã ví dụ được nhắc trong bài viết. Bạn chỉ cần truy cập mục tải xuống đặt ở phía trên của trang.

    Chuyển đổi từ điển thành mảng

    Chúng ta đã biết rằng từ điển bao gồm cặp Key và Giá trị. Các Key của từ điển tạo thành một mảng chứa tất cả key, và các Item cũng tạo thành một mảng chứa tất cả các giá trị.

    Do cả hai đều là mảng, việc chúng ta viết chúng vào bảng tính là điều có thể, như sẽ được giải thích ở phần sau.  

    Để chuyển các mảng Key hoặc Item vào một mảng mới, phương pháp thực hiện rất đơn giản như sau:

    Dim arr As Variant
    arr = dict.Keys

    Chi tiết việc sao chép các mảng Keys Items ra khỏi từ điển và sau đó xuất chúng ra cửa sổ Immediate Window được mô tả như sau:

    Sub DictionaryToArray()
        
        ' Tạo từ điển và thêm dữ liệu
        Dim dict As New Dictionary
        dict.Add "France", 56
        dict.Add "USA", 23
        dict.Add "Australia", 34
    
        ' Khai báo biến dạng mảng
        Dim arr As Variant
    
        ' Chuyển key vào mảng
        arr = dict.Keys
        ' Xuất mảng ra cửa sổ Immediate (Ctrl + G để xem)
        Call PrintArrayToImmediate(arr, "Keys:")
        
        ' Chuyển item vào mảng
        arr = dict.Items
        ' Xuất mảng ra cửa sổ Immediate (Ctrl + G để xem)
        Call PrintArrayToImmediate(arr, "Items:")
    
    End Sub
    
    ' Hàm xuất mảng ra cửa sổ Immediate (Ctrl + G để xem)
    Sub PrintArrayToImmediate(arr As Variant, headerText As String)
        
        Debug.Print vbNewLine & headerText
        Dim entry As Variant
        For Each entry In arr
            Debug.Print entry
        Next
            
    End Sub

    Kết quả đạt được khi chạy mã:

    Chú ý là chỉ có thể chuyển các mảng Items nếu chúng chứa dữ liệu cơ bản như chuỗi, số nguyên, ngày tháng, số thực, và lý do là bạn không thể chuyển nếu các item là đối tượng. Trong trường hợp đó, bạn cần phải dùng đến vòng lặp để duyệt từ điển.

    Ghi từ điển vào bảng tính

    Chúng ta cũng có thể chuyển các key hoặc item của từ điển vào bảng tính chỉ qua một dòng lệnh duy nhất.

    Các key hoặc item, khi ghi vào bảng, sẽ nằm trên một dòng. Để viết chúng dọc theo một cột, bạn nên dùng hàm WorksheetFunction.Transpose.

    Sub DictionaryToWorksheet()
        
        Dim dict As New Dictionary
        
        dict.Add "France", 56
        dict.Add "USA", 23
        dict.Add "Australia", 34
        
        Dim sh As Worksheet
        Set sh = ThisWorkbook.Worksheets("Sheet1")
        
        ' Ghi key vào vùng A1:C1
        sh.Range("A1:C1").Value = dict.Keys
        
        ' Ghi item vào vùng A2:C2
        sh.Range("A2:C2").Value = dict.Items
        
        ' Ghi key dọc vùng E1:E3
        sh.Range("E1:E3").Value = WorksheetFunction.Transpose(dict.Keys)
        
        ' Ghi item dọc vùng F1:F3
        sh.Range("F1:F3").Value = WorksheetFunction.Transpose(dict.Items)
    
    End Sub

    Mã dưới đây mô tả cách viết từ điển ra bảng tính:

    Ví dụ thực tế áp dụng từ điển

    Để hiểu rõ hơn về lợi ích của từ điển, hãy tham khảo một số ví dụ sau. Bạn có thể tải mã nguồn và bảng làm việc cho những ví dụ dưới đây bằng cách nhập địa chỉ email của bạn:

    Ví dụ 1 – Tính tổng giá trị đơn hàng

    Xét ví dụ thực tế về cách dùng từ điển trong việc tổng hợp dữ liệu. Dữ liệu cho ví dụ này bao gồm:

    ...

    Đây là tổng hợp của các cuộc đối đầu tại Chung kết World Cup kể từ năm 2014.

    Nhiệm vụ chính ở đây là thu thập số lượng bàn thắng mà mỗi đội đã ghi được.

    Bước ban đầu cần thực hiện là xem xét toàn bộ thông tin. Mã dưới đây cho phép quét qua các trận đấu và xuất ra tên của các đội tham gia.

    Điều tiếp theo mà chúng ta cần làm là ghi lại từng đội và số bàn thắng họ đã ghi. Nếu một đội xuất hiện lần đầu, chúng ta sẽ thêm tên họ vào Key và số bàn thắng vào Item.

    Khi gặp lại đội đã có sẵn, chúng ta sẽ cập nhật tổng số bàn thắng dựa trên số bàn thắng ghi được trong lần so tài hiện tại.

    Để thực hiện điều này, ta có thể dùng dòng lệnh sau để cập nhật cho đội:

    Dòng này hoạt động vô cùng hiệu quả.

    Khi đội đã có trong Từ điển, nó sẽ thêm bàn thắng mới vào số bàn thắng mà đội đó đã có. Nếu đội chưa nằm trong Từ điển, nó sẽ tự động tạo một mục mới và thiết lập giá trị cho số bàn thắng.

    Xét cho một ví dụ, giả sử Từ điển hiện có mục

    Key, Value

    Brazil, 5

    Thực hiện dòng lệnh đề cập ở trên

    sẽ cải tiến Từ điển để nó hiển thị thành

    Key, Value

    Brazil, 8

    Pháp, 3

    Công thức này giúp ta tránh việc viết mã bằng cách sau:

    Chép các giá trị từ Từ điển ra bảng tính như sau:

    ‘ Dữ liệu từ Từ điển được ghi ra bảng tính

    ‘ https://excelmacromastery.com/vba-dictionary

    Private Sub WriteDictionary(dict As Scripting.Dictionary, shReport As Worksheet)

        ClearData shReport

        ‘ Ghi các khóa

        shReport.Range( “A1” ).Resize(dict.Count, 1).Value = WorksheetFunction.Transpose(dict.Keys)

        ‘ Ghi các mục

        shReport.Range( “B1” ).Resize(dict.Count, 1).Value = WorksheetFunction.Transpose(dict.Items)

    End Sub

    Chắc chắn, chúng ta mong muốn điểm số được sắp xếp theo trình tự, bởi nó dễ đọc hơn. Tuy nhiên, Từ điển không thể sắp xếp một cách đơn giản. Ta có thể chuyển tất cả các mục vào một mảng, sắp xếp mảng đó rồi sau đó chép lại vào Từ điển.

    Một giải pháp khác là sắp xếp dữ liệu sau khi chúng đã được nhập vào bảng tính. Mã sau đây có thể giúp chúng ta làm điều đó:

    Sub cuối cùng của chúng ta GetTotals sẽ có dạng như sau:

    Khi bạn chạy đoạn mã này, kết quả thu được sẽ là:

    Ví dụ 2 – Xử lý đa giá trị

    Bây giờ chúng ta sẽ xem xét dữ liệu từ phần Đa giá trị mà chúng ta đã nhắc đến trước đó.

    Giả định dữ liệu bắt đầu tại ô A1, sau đó ta có thể dựa vào mã sau đây để nhập liệu vào Từ điển.

    Mã này được bao gồm hai quy trình để hiển thị dữ liệu:

    1. WriteToImmediate viết ra nội dung của Từ điển trong Immediate Window.
    2. WriteToWorksheet ghi nội dung của Từ điển ra một trang tính mang tên Output.

    Để thực thi ví dụ này:

    1. Tạo một trang tính với tên Khách hàng.
    2. Đưa dữ liệu vào trang tính từ ô A1.
    3. Tạo một trang tính mới với tên Output và để trống.
    4. Chuyển đến Visual Basic Editor (Alt + F11).
    5. Lựa chọn Tools->Reference và chọn "Microsoft Scripting Runtime" tự danh sách.
    6. Tạo ra một module.
      1. Thêm lớp mới và chèn đoạn mã đầu tiên theo mô tả dưới đây.
      2. Tạo mô-đun mới chuẩn và chèn đoạn mã thứ hai như hướng dẫn sau.
      3. Bấm F5 để thực thi chương trình và chọn Main trong menu.
      4. Kiểm tra ImmediateWindow (Ctrl + G) cùng trang tính Output để xem kết quả.

      Ví dụ 3 – Tổng hợp nhiều giá trị

      Ở ví dụ sau, chúng ta sẽ tinh chỉnh Ví dụ 2 một chút. Tại ví dụ trước, mỗi khách hàng chỉ xuất hiện một lần trong dữ liệu.

      Lúc này mỗi khách hàng sẽ xuất hiện nhiều lần và mục tiêu là tổng hợp lại số lượng Amount và Items đối với mỗi khách hàng.

      Xem bộ dữ liệu đã được cập nhật ở dưới:

      Chú ý: Nếu bạn thực hiện "Example 2" với dữ liệu mà CustomerID xuất hiện nhiều lần, bạn sẽ nhận được lỗi "Key already exists error".

      Khi nào nên dùng từ điển VBA

      Cần sử dụng VBA Dictionary trong những trường hợp nào? Khi bạn đối mặt với nhiệm vụ cần:

      1. Một danh sách các mục độc nhất, ví dụ như: tên của các quốc gia, số của hóa đơn, tên khách hàng và địa chỉ, mã dự án, tên sản phẩm, etc.
      2. Lấy ra giá trị của một mặt hàng độc lập.
      Trương Thành Tài
0
    0
    Đơn hàng
    Đơn hàng trốngQuay lại Shop