Học Dictionary qua các ví dụ đơn giản!

Liên hệ QC

chuot0106

Thành viên gắn bó
Tham gia
20/1/13
Bài viết
2,567
Được thích
1,670
Thực sự thì mình cũng chưa biết tên topic như vậy có hợp lí không(Nếu chưa hợp lí mong BQT sửa giúp), mình nêu mục đích của topic này luôn.
Bởi vì trên GPE đã có topic về vấn đề này rồi tuy nhiên các topic đó cũng chưa đi sâu lắm về "Dic" bản thân mình rất khó tiếp thu(cá nhân mình thôi). bởi vậy mình xin phép BQT được lập Topic mới này giành cho những người mới chập chững nghiên cứu về "Dic" như mình với mục đích chính như sau:
+ Mong các bạn có kinh nghiệm về "Dic" vào chia sẻ kinh nghiệm của bả n thân về việc học "Dic".
+ Các bạn có kinh nghiệm về "Dic" đưa ra các bài tập từ cơ bản đến đến nâng cao để các thành viên mới thực hành.
+ Các thành viên mới có thể đưa ra các câu hỏi cũng như bài tập liên quan để các thành viên có kinh nghiệm giúp đỡ.

Tất cả các mục đích này dựa trên tinh thần chia sẻ, giao lưu, học hỏi.
Rất mong nhận được sự chia sẻ của các bạn!
 
Nếu cho sẵn dữ liệu tại C1 thì mình sẽ xử theo cách này. Thuật toán mình thì tà đạo là chính
Dùng sự kiện change để chạy code này
PHP:
Sub abc()
Dim nguon(), kq(1 To 10000, 1 To 1), i, k, dk
dk = [C1]
nguon = Range([A2], [B65536].End(3)).Value
With CreateObject("scripting.dictionary")
    For i = 1 To UBound(nguon)
        If Not .exists(nguon(i, 1)) Then
            .Add nguon(i, 1), ""
            If nguon(i, 2) <> dk Then
                k = k + 1
                kq(k, 1) = nguon(i, 1)
            End If
        End If
    Next
End With
[D2].Resize(UBound(kq)) = kq
End Sub
 
Upvote 0
....................................... đang sửa code
 
Lần chỉnh sửa cuối:
Upvote 0
Nếu muốn lấy hết 1 lần thì thử code này. Mình thì mần được hết nhưng thuật toán code thì tè le.
PHP:
Sub abc()
Dim nguon(), kq(), i, j, k, tam()
nguon = Range([A2], [B65536].End(3)).Value
With CreateObject("scripting.dictionary")
    For i = 1 To UBound(nguon)
        If Not .exists(nguon(i, 2)) Then
            .Add nguon(i, 2), ""
        End If
    Next
    tam = .keys
    ReDim kq(1 To 1000, 1 To UBound(tam) + 1)
    For j = 0 To UBound(tam)
        k = 0
        With CreateObject("scripting.dictionary")
            For i = 1 To UBound(nguon)
                If Not .exists(nguon(i, 1)) Then
                    .Add nguon(i, 1), ""
                    If nguon(i, 2) <> tam(j) Then
                        k = k + 1
                        kq(k, j + 1) = nguon(i, 1)
                    End If
                End If
            Next
        End With
    Next
End With
[C1].Resize(, j) = tam
[C2].Resize(k, j) = kq
End Sub
Trong khi chờ thuật toán tối ưu hơn thì những thuật toán tè le như anh quá hữu ích luôn! Đôi khi em mong nghĩ ra những cái tè le như anh mà còn ko được đó.
P/s: Điều quan trọng trước hết là mình phải xử lí vấn đề trước mắt đã.
 
Lần chỉnh sửa cuối:
Upvote 0
Nếu cho sẵn dữ liệu tại C1 thì mình sẽ xử theo cách này. Thuật toán mình thì tà đạo là chính
Dùng sự kiện change để chạy code này
PHP:
Sub abc()
Dim nguon(), kq(1 To 10000, 1 To 1), i, k, dk
dk = [C1]
nguon = Range([A2], [B65536].End(3)).Value
With CreateObject("scripting.dictionary")
    For i = 1 To UBound(nguon)
        If Not .exists(nguon(i, 1)) Then
            .Add nguon(i, 1), ""
            If nguon(i, 2) <> dk Then
                k = k + 1
                kq(k, 1) = nguon(i, 1)
            End If
        End If
    Next
End With
[D2].Resize(UBound(kq)) = kq
End Sub
Không phải dữ liệu cho trước ở C1 mà khi nhập vào C1 thì sẽ cho kết quả ở cột C từ C2 trở đi anh ạ!
 
Upvote 0
Thực ra bạn chỉ bí về thuật toán còn các thuộc tính và phương thức của dic bạn nắm gọn trong lòng bàn tay rồi còn gì.
Chính vì thế tôi mới nhấn mạnh: thuật toán là cái khó và phải học mãi. Rèn luyện tư duy.

Có thể có nhiều hướng, tôi nghĩ đơn giản thế này. Ta duyệt cột A và cho vào dic những tên thành phố. Kết quả phải đạt được là: những tên trong dic mà C1 đã đến thì có Item = 1, những tên chưa đến thì có Item = 0. Sau khi duyệt hết cột A (có thể A, B đập vào mảng trước đó) thì đọc ra những Key mà có Item = 0. Thế thôi.
Tôi không chỉ rõ cách thêm Key vào dic như thế nào vì nếu chỉ ra thì xong bài toán rồi còn gì.
Quả thật đây là bài toán khá hóc búa, thầy đã gợi ý như vậy mã nghĩ mãi từ tối qua tới giờ vẫn chưa ra? Cái khó là không biết chọn Key.
 
Upvote 0
Thêm một thuật toán gợi ý cho bạn nữa:
Dùng hai lần lặp cho cùng mảng dữ liệu:
- Vòng lặp thứ nhất, tìm tên các TP mà [C1] đã đi qua đưa vào Dic1
- Vòng lặp thứ hai nếu TP nào chưa có trong Dic1 thì đưa vào Dic2
Danh sách cần tìm sẽ nằm trong Dic2
 
Upvote 0
Thêm một thuật toán gợi ý cho bạn nữa:
Dùng hai lần lặp cho cùng mảng dữ liệu:
- Vòng lặp thứ nhất, tìm tên các TP mà [C1] đã đi qua đưa vào Dic1
- Vòng lặp thứ hai nếu TP nào chưa có trong Dic1 thì đưa vào Dic2
Danh sách cần tìm sẽ nằm trong Dic2
Thêm 1 kinh nghiệm nữa về dùng Dic, tức là chúng ta có thể khai báo đồng thời 2 biến Dic1 và Dic2 cũng được phải không anh?
 
Upvote 0
Wow, Theo mình hiểu, nó là một đối tượng (Object) Dictionary trong VBA, cũng như file trong hệ điều hành thôi, vì vậy bạn tạo ra bao nhiêu cái không được (chỉ sợ tràn bộ nhớ, he he) còn Díc, Dic2 ... chẳng qua là những cái tên.
 
Upvote 0
Thêm 1 kinh nghiệm nữa về dùng Dic, tức là chúng ta có thể khai báo đồng thời 2 biến Dic1 và Dic2 cũng được phải không anh?

Dic có kiểu là object. Mỗi lần kêu hàm CreateObject (nếu có reference thì là lệnh New) thì tạo được 1 object. Dùng một lúc 1 triệu cũng được nếu bộ nhớ cho phép.

Dùng lý thuyết CSDL liên hệ. Lập tất cả 3 dic's
D1 chứa các tên tỉnh
D2 chứa các tên người
D3 chứa các tỉnh mà người đã đi qua, tức là tên tỉnh & tên người (đương nhiên phải cách cái gì đó, ";" chẳng hạn)
(Các Dic's trên đều chỉ dùng key, không dùng item. Chỉ cần một vòng lặp là dựng đủ 3 dic's)
VD muốn tìm tên các tỉnh mà chị A chưa/đã đến thì dùng một vòng lặp duyệt D1, tìm tên tỉnh & ";" & "A" trong D3, nếu có thì là đã nếu không thì là chưa.

Cách này tương đối chậm nhưng khá giản dị khoa học. Đúng với tinh thần CSDL LH, giản dị khoa học nhưng chậm.

Cách thứ hai là cách phản chuẩn của CSDL LH:
D1 có key là tỉnh và item là chuỗi chứa các tên tương ứng
Muôn biết chị A chưa/đã đi qua những tỉnh nào thì duyệt D1, xét tên "A" trong chuỗi item.
 
Lần chỉnh sửa cuối:
Upvote 0
Quả thật đây là bài toán khá hóc búa, thầy đã gợi ý như vậy mã nghĩ mãi từ tối qua tới giờ vẫn chưa ra? Cái khó là không biết chọn Key.
Key là các thành phố mà bạn.

Sub cau5()
Dim r As Range
Dim dic As Scripting.Dictionary
Dim s As String, k As String
Dim i As Integer
Dim j
s = Range("c1").Text
Set dic = New Scripting.Dictionary
Set r = Range("a2", Range("b100").End(xlUp))
For i = 1 To r.Rows.Count
k = r.Cells(i, 1).Text
If Not (dic.Exists(k)) Then dic.Add k, False
If r.Cells(i, 2).Text Like (s & "*") Then dic.Item(k) = True
Next
i = 2
Range("c2:c100").ClearContents
For Each j In dic
If Not (dic.Item(j)) Then
Cells(i, 3) = j
i = i + 1
End If
Next
End Sub
 

File đính kèm

  • Cauhoi5_Dic.xls
    24.5 KB · Đọc: 21
Lần chỉnh sửa cuối:
Upvote 0
Đi xa mục tiêu đề bài. Xin phép xoá để khỏi làm loãng thớt. Xin lỗi.
 
Lần chỉnh sửa cuối:
Upvote 0
Nhất định dùng 1 Dic thử coi có ra kết quả không. Đau đầu quá
PHP:
Sub abc()
Dim nguon(), kq(), i, j, k, tam()
nguon = Range([A2], [B65536].End(3)).Value
With CreateObject("scripting.dictionary")
    For i = 1 To UBound(nguon)
        If Not .exists(nguon(i, 2)) Then
            .Add nguon(i, 2), ""
        End If
    Next
    tam = .keys
End With
ReDim kq(1 To 1000, 1 To UBound(tam) + 1)
For j = 0 To UBound(tam)
     With CreateObject("scripting.dictionary")
        k = 0
        For i = 1 To UBound(nguon)
            If nguon(i, 2) = tam(j) Then
                If Not .exists(nguon(i, 1)) Then
                    .Add nguon(i, 1), ""
                End If
            End If
        Next
        For i = 1 To UBound(nguon)
            If nguon(i, 2) <> tam(j) Then
                If Not .exists(nguon(i, 1)) Then
                    .Add nguon(i, 1), ""
                    k = k + 1
                    kq(k, j + 1) = nguon(i, 1)
                End If
            End If
        Next
    End With
Next
[C1].Resize(, j) = tam
[C2].Resize(UBound(kq), j) = kq
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Nhất định dùng 1 Dic thử coi có ra kết quả không. Đau đầu quá
PHP:
Sub abc()
Dim nguon(), kq(), i, j, k, tam()
nguon = Range([A2], [B65536].End(3)).Value
With CreateObject("scripting.dictionary")
    For i = 1 To UBound(nguon)
        If Not .exists(nguon(i, 2)) Then
            .Add nguon(i, 2), ""
        End If
    Next
    tam = .keys
End With
ReDim kq(1 To 1000, 1 To UBound(tam) + 1)
For j = 0 To UBound(tam)
     With CreateObject("scripting.dictionary")
        k = 0
        For i = 1 To UBound(nguon)
            If nguon(i, 2) = tam(j) Then
                If Not .exists(nguon(i, 1)) Then
                    .Add nguon(i, 1), ""
                End If
            End If
        Next
        For i = 1 To UBound(nguon)
            If nguon(i, 2) <> tam(j) Then
                If Not .exists(nguon(i, 1)) Then
                    .Add nguon(i, 1), ""
                    k = k + 1
                    kq(k, j + 1) = nguon(i, 1)
                End If
            End If
        Next
    End With
Next
[C1].Resize(, j) = tam
[C2].Resize(UBound(kq), j) = kq
End Sub
Anh ơi code này nó thống kê hết cả ra. Em chỉ muốn khi nào nhập vào C1 tên ai đó thì nó mới thống kê thôi thì phải sửa thế nào vậy anh?
 
Upvote 0
Anh ơi code này nó thống kê hết cả ra. Em chỉ muốn khi nào nhập vào C1 tên ai đó thì nó mới thống kê thôi thì phải sửa thế nào vậy anh?

Thử code này coi sao
PHP:
Sub ABC2()
Dim nguon(), kq(1 To 1000, 1 To 1), i, j, k, dk
dk = [C1]
nguon = Range([A2], [B65536].End(3)).Value
With CreateObject("scripting.dictionary")
    For i = 1 To UBound(nguon)
        If nguon(i, 2) = dk Then
            If Not .exists(nguon(i, 1)) Then
                .Add nguon(i, 1), ""
            End If
        End If
    Next
    For i = 1 To UBound(nguon)
        If Not .exists(nguon(i, 1)) Then
            k = k + 1
            .Add nguon(i, 1), ""
            kq(k, 1) = nguon(i, 1)
        End If
    Next
End With
[C2].Resize(k) = kq
End Sub
 
Upvote 0
Thử code này coi sao
PHP:
Sub ABC2()
Dim nguon(), kq(1 To 1000, 1 To 1), i, j, k, dk
dk = [C1]
nguon = Range([A2], [B65536].End(3)).Value
With CreateObject("scripting.dictionary")
    For i = 1 To UBound(nguon)
        If nguon(i, 2) = dk Then
            If Not .exists(nguon(i, 1)) Then
                .Add nguon(i, 1), ""
            End If
        End If
    Next
    For i = 1 To UBound(nguon)
        If Not .exists(nguon(i, 1)) Then
            k = k + 1
            .Add nguon(i, 1), ""
            kq(k, 1) = nguon(i, 1)
        End If
    Next
End With
[C2].Resize(k) = kq
End Sub
Quá ổn rồi anh ạ! Quá sợ với cái bài toán của thầy Siwtom!-\\/.
 
Upvote 0
Ở phần trọng tâm thì tôi làm giống bạn Hau151978, chỉ khác về chi tiết. Tức như đã nói tôi cho Item = 0/1. Tôi dùng IF ... Else thay cho 2 IF. Và tôi tìm đúng, tức C1 thế nào thì tìm đúng, do vậy không dùng LIKE.
Mã:
name = LCase([C1].Value)
...
Set dic = CreateObject("Scripting.Dictionary")
For k = 1 To UBound(Arr)
    If LCase(Arr(k, 2)) <> name Then
        If Not dic.exists(Arr(k, 1)) Then dic.Add Arr(k, 1), 0    ' <-- A
    Else
        dic.Item(Arr(k, 1)) = 1                ' <-- B
    End If
Next

Ngoài ra còn: xóa dữ liệu cũ, nếu cột A không có dữ liệu thì không làm gì cả. Nhưng những cái này không thuộc trọng tâm bài Toán, tôi chỉ ra bài về dic thôi.

Đã có lời giải nhưng không phải của bạn chuot0106, vậy thì những câu hỏi sau đây là dành cho bạn chuot0106. Yêu cầu người khác không trả lời, vì thực ra câu hỏi dễ, chỉ dành cho người đang "vọc" thôi.

1. Tại sao tôi không dùng ở A cấu trúc giống như ở B, tức
Mã:
dic.Item(Arr(k, 1)) = 0

2. Tại sao tại A và B tôi không dùng cấu trúc
Tại A
Mã:
If Not dic.exists(Arr(k, 1)) Then 
    dic.Item(Arr(k, 1)) = 0
else
    dic.Item(Arr(k, 1)) = 0
end if

Tại B
Mã:
If Not dic.exists(Arr(k, 1)) Then 
    dic.Item(Arr(k, 1)) = 1
else
    dic.Item(Arr(k, 1)) = 1
end if

Dùng tại A như trên được không? Dùng tại B như trên được không? Nếu được/không được (cùng lắm thì chạy code là biết) thì giải thích tại sao được/không được.
 
Upvote 0
Thực ra bạn chỉ bí về thuật toán còn các thuộc tính và phương thức của dic bạn nắm gọn trong lòng bàn tay rồi còn gì.
Chính vì thế tôi mới nhấn mạnh: thuật toán là cái khó và phải học mãi. Rèn luyện tư duy.

Có thể có nhiều hướng, tôi nghĩ đơn giản thế này. Ta duyệt cột A và cho vào dic những tên thành phố. Kết quả phải đạt được là: những tên trong dic mà C1 đã đến thì có Item = 1, những tên chưa đến thì có Item = 0. Sau khi duyệt hết cột A (có thể A, B đập vào mảng trước đó) thì đọc ra những Key mà có Item = 0. Thế thôi.
Tôi không chỉ rõ cách thêm Key vào dic như thế nào vì nếu chỉ ra thì xong bài toán rồi còn gì.
Thầy có thể cho em xin dòng code đọc ra những key có item =0 được không ạ!
 
Upvote 0
Thầy có thể cho em xin dòng code đọc ra những key có item =0 được không ạ!
Thí nghiệm nhiều sẽ thấy thôi. Làm 1 vòng lặp cho tất cả các key, nếu key nào có item = 0 thì "hốt" key đó vào một mảng 1 chiều, khai báo mảng đó bằng ReDim Preserve để mảng đó giữ tất cả các key có điều kiện đó, chỉ vậy thôi.
 
Upvote 0
Thí nghiệm nhiều sẽ thấy thôi. Làm 1 vòng lặp cho tất cả các key, nếu key nào có item = 0 thì "hốt" key đó vào một mảng 1 chiều, khai báo mảng đó bằng ReDim Preserve để mảng đó giữ tất cả các key có điều kiện đó, chỉ vậy thôi.
Do các công cụ của dic e chưa nắm hết nên học rất khó khăn. Cũng tại ít tài liệu nói về dic quá!
 
Upvote 0
Web KT
Back
Top Bottom