Xin hỏi về cách sử dụng Dictionary (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

thaitungchi

Thành viên chính thức
Tham gia
20/1/17
Bài viết
55
Được thích
19
Xin các bác cho hỏi có cách nào điền giá trị lưu trong mydict (Dictionary) ra một cell nào đó, ví dụ A1:A.

Mặc dù đã có tham khảo cách sử dụng Dictionary trên diễn đàn rồi nhưng mà chưa có hướng dẫn chi tiết, hoặc là khả năng VBA còn hạn chế quá.



Mong các cao thủ ra tay cứu giúp.



PHP:
Sub Sample4()

   Dim mydict As Object

   Set mydict = CreateObject("Scripting.Dictionary")



   Dim c As Range

   Dim firstAddress As String

   Dim dk1 As Variant

   Dim dk2 As Variant



   dk1 = Worksheets("Memberlist").Range("D4").Value

   dk2 = Worksheets("Memberlist").Range("D6").Value



   With Worksheets("OrderList").Range("C9:C370")

       Set c = .Find(dk1, LookIn:=xlValues, LookAt:=xlPart)

           If Not c Is Nothing Then

               firstAddress = c.Address

               If dk2 = "" Then

                   MsgBox ("Please Input Worktype")

               Else

                   Do

                       If dk2 = c.Offset(0, 1).Value Then

                           mydict.Add c.Offset(0, -2).Value, ""

                           MsgBox ("Gia tri la" & c.Offset(0, -2).Value)

                       End If

                       Set c = .FindNext(c)

                       If c Is Nothing Then Exit Do        Post thread

                   Loop Until c.Address = firstAddress

               End If

           End If

   End With





End Sub
 
Xin các bác cho hỏi có cách nào điền giá trị lưu trong mydict (Dictionary) ra một cell nào đó, ví dụ A1:A.

Mặc dù đã có tham khảo cách sử dụng Dictionary trên diễn đàn rồi nhưng mà chưa có hướng dẫn chi tiết, hoặc là khả năng VBA còn hạn chế quá.



Mong các cao thủ ra tay cứu giúp.



PHP:
Sub Sample4()

   Dim mydict As Object

   Set mydict = CreateObject("Scripting.Dictionary")



   Dim c As Range

   Dim firstAddress As String

   Dim dk1 As Variant

   Dim dk2 As Variant



   dk1 = Worksheets("Memberlist").Range("D4").Value

   dk2 = Worksheets("Memberlist").Range("D6").Value



   With Worksheets("OrderList").Range("C9:C370")

       Set c = .Find(dk1, LookIn:=xlValues, LookAt:=xlPart)

           If Not c Is Nothing Then

               firstAddress = c.Address

               If dk2 = "" Then

                   MsgBox ("Please Input Worktype")

               Else

                   Do

                       If dk2 = c.Offset(0, 1).Value Then

                           mydict.Add c.Offset(0, -2).Value, ""

                           MsgBox ("Gia tri la" & c.Offset(0, -2).Value)

                       End If

                       Set c = .FindNext(c)

                       If c Is Nothing Then Exit Do        Post thread

                   Loop Until c.Address = firstAddress

               End If

           End If

   End With





End Sub
File của bạn đâu?
 
Xin lỗi vì để bác đợi lâu. Đây là file excel đính kèm.
Không chỉ liệt kê mã số đơn hàng (Order No.) mà còn thành viên đã tham gia vào dự án đấy.

Kết quả có hiển thị ở sheet Memberlist, từ B12:C38.

Mong các cao thủ excel cứu giúp.
 

File đính kèm

Ủa vậy thì bạn add vào dic như thế nào
 
Cũng đã thử cho vào Dic ở dòng lệnh này. Có khi là chưa đúng.

mydict.Add c.Offset(0, -2).Value, ""

Chắc phải kiểm tra bằng cách đếm thành phần của Dic
 
Từ đầu chí cuối bạn có cho người ta biết bạn muốn làm gì đâu?
Ai biết ai chết mà "cứu".

Cái này cầu "cao thủ" làm gì, gọi "mò thủ" may ra mù thỏ trúng.
 
Từ đầu chí cuối bạn có cho người ta biết bạn muốn làm gì đâu?
Ai biết ai chết mà "cứu".

Cái này cầu "cao thủ" làm gì, gọi "mò thủ" may ra mù thỏ trúng.
Thực ra cũng có thể dò, kiểm tra thì sẽ đoán được. Nhưng tôi rất ngại với những người như thế. Mình có thể nói ra hết nhưng mình không nói, kệ người khác dò, kiểm tra và đoán ý.
mà còn thành viên đã tham gia vào dự án đấy
Để là việc đó thì phải xét những sheet nào? Với mỗi sheet phải tìm cái gì, ở đâu, kiểm tra thế nào, nêm gia vị ra sao, xào nấu theo công nghệ nào để ra "thành viên đã tham gia vào dự án đấy".
Có thể nhiều khi việc đoán mò rất khó khăn, đôi khi có thể rất dễ dàng. Tôi chỉ nói về nguyên tắc. Nguyên tắc của tôi là: nếu có thể nói được thì phải nói ra. Người giúp mình không phải đoán gì cả. Thế thôi.
 
Hỗ trợ cách hỏi 1 chút:
- Câu hỏi bài 1 là "có cách nào điền giá trị lưu trong mydict (Dictionary) ra một cell nào đó" vậy thì giá trị trong mydict sau khi chạy chỉ có 3 giá trị là
1738
1778
1885
- Kết quả mong muốn nhìn thấy lại đến mấy chục dòng. (dù chỉ liên quan đến 3 giá trị trên)
Vậy phải mô tả mấy chục dòng đó lấy từ đâu ra.
 
Xin lỗi mọi người vì giải thích thiếu sót nhiều quá. Giải thích chi tiết như sau:

Đây là file quản lý danh sách đơn hàng. Như mọi người nhìn thấy trong sheet “Order List”, có một loạt đơn hàng có mã số (1738, 1739, 1740, 1885, v.v...)

Các đơn hàng sẽ có tên mã dự án (projectA, B...), loại công việc (worktype), thời gian bắt đầu, thời gian kết thúc, số ngày công. Các thông tin này sẽ lấy từ sheet “4-9” và sheet “10-3”.

Trong sheet “4-9” và sheet “10-3” này cấu trúc là chiều ngang là ngày tháng và chiều dọc là danh sách thành viên (tên nhân viên, mã số nhân viên). Ví dụ sheet “4-9” thì sẽ chiều ngang từ ngày 1/4 đến 30/9, sheet “10-3” sẽ là từ 1/10 đến 31/3. Hai sheet này sẽ chứa thông tin, thành viên nào tham gia vào đơn hàng nào, từ ngày nào đến ngày nào. Tại mỗi một ô trong bảng này, có ghi mã số đơn hàng tức là thành viên đấy, vào ngày đấy đã có tham gia vào đơn hàng đấy.

Ví dụ: Thành viên TKF0004, có mã số 304095 tham gia vào đơn hàng 1738 từ 1/4 đến 30/5. Có ba thành viên khác nữa là TKF00021, TKF00027, TKF00029 cũng tham gia vào đơn hàng 1738 từ ngày 1/4 đến 6/5.

Đề bài ở đây là: tại sheet “memberlist”, khi nhập tên dự án (khác với mã số đơn hàng) và loại công việc (worktype) thì sẽ liệt kê ra danh sách các đơn hàng có cùng tên dự án và loại công việc.

Ngoài ra, trong dự án này có bao nhiêu thành viên cũng liệt kê ra hết. Tại vì có những thành viên tên giống nhau nên khi lọc ra tên thành viên nên tìm theo mã số nhân viên.

Ví dụ: khi nhập tên dự án “ProjectA”, loại công việc 212H, dựa vào sheet “Oderlist” sẽ biết được ba đơn hàng đáp ứng được thông tin trên 1738, 1778, 1885.

Sau đó dựa vào 3 mã số đơn hàng, tại sheet “4-9” và sheet “10-3” tìm tất cả thành viên tham gia vào đơn hàng này và liệt kê ra từ ô B12

Đơn hàng 1738 thì có 4 thành viên, 1778 có 6 thành viên, và 1885 có 9 thành viên (TKF0020 bị trùng nhau giữa hai sheet). Những thành viên này cũng được liệt kê hết ra là thoã mãn yêu cầu đề bài.

Giải thích khá dài dòng mong mọi người thông cảm.

Nếu có điều gì chưa rõ ràng, mọi người cứ hỏi thêm.
Cám ơn mọi người dành thời gian quan tâm.

P/S: Xong đợt này quyết tâm mua sách VBA về học thêm mảng và dictionary. Chắc là giải pháp excel có những sách nào hướng dẫn chi tiêt và cụ thể về vấn đề này nhỉ.
 
Dictionary (gọi tắt là Dic dưới đây) là bảng tra theo quy luật key-item. Tức là phép chiếu từng phần tử tập hợp A sang tập hợp B.

Công dụng chính của Dic là bảng tra.
Vì Dic dùng kỹ thuật tra key (hầu hết các thết kế dùng bảng băm) rất hiệu quả cho nên lập bảng tra bằng Dic giúp cải tiến tốc độ chương trình. Lưu ý là phải tra nhiều lần mới tính chuyện hiệu quả; chứ tra có một lần mà bảo phải lập nguyên cái Dic để tra thì là làm chuyện thừa thải.
Và vì luật key-item bắt buộc các phần tử key phải là duy nhất cho nên dân lập trình cũng lợi dụng tính chất này để "lọc duy nhất"

Trước khi tiếp tục với đề bài của bạn thì bạn cho biết vì sao bạn chọn Dic để thực hiện giải thuật của mình.
Chứ đọc cái trọng tâm đề bài "nhập tên dự án, và ... thì sẽ hiện ra..." tôi chưa thấy nó liên quan đến Dic.
 
Xin lỗi vì để bác đợi lâu. Đây là file excel đính kèm.
Không chỉ liệt kê mã số đơn hàng (Order No.) mà còn thành viên đã tham gia vào dự án đấy.

Kết quả có hiển thị ở sheet Memberlist, từ B12:C38.

Mong các cao thủ excel cứu giúp.
Code khá phức tạp
Mã:
Sub xyz()
  Dim sArr(), Res(), shName, Dic As Object, iKey$, iKey2$
  Dim eRow&, eCol&, i&, n&, ik&, j&
  Dim DuAn$, cViec$
  Const sNV As Long = 100 'toi da 100 nhan vien
 
  shName = Array("4-9", "10-3")
  Set Dic = CreateObject("Scripting.Dictionary")
  With Sheets("Memberlist")
    .Range("B12:C1000").ClearContents
    DuAn = .Range("D4").Value
    cViec = .Range("D6").Value
    If DuAn = Empty Or cViec = Empty Then Exit Sub
  End With
  With Sheets("Order_List")
    sArr = .Range("A9:D370").Value
    For i = 1 To UBound(sArr)
      If sArr(i, 3) = DuAn And sArr(i, 4) = cViec Then
        iKey = sArr(i, 1)
        Dic.Item(iKey) = k
        k = k + sNV
      End If
    Next i
  End With
  ReDim Res(1 To k, 1 To 2)
  For n = 0 To UBound(shName)
    With Sheets(shName(n))
      eRow = .Range("C" & Rows.Count).End(xlUp).Row
      eCol = .Cells(4, 16000).End(xlToLeft).Column
      sArr = .Range("B5", .Cells(eRow, eCol)).Value
      For i = 1 To UBound(sArr)
        For j = 6 To UBound(sArr, 2)
          iKey = sArr(i, j)
          If Dic.exists(iKey) Then
            iKey2 = sArr(i, 1) & "#" & iKey
            If Dic.exists(iKey2) = False Then
              Dic.Add iKey2, ""
              ik = Dic.Item(iKey) + 1
              Dic.Item(iKey) = ik
              Res(ik, 1) = iKey
              Res(ik, 2) = sArr(i, 2)
            End If
          End If
        Next j
      Next i
    End With
  Next n
  k = 0
  For i = 1 To UBound(Res)
    If Res(i, 1) <> Empty Then
      k = k + 1
      Res(k, 1) = Res(i, 1)
      Res(k, 2) = Res(i, 2)
    End If
  Next i
  If k Then Sheets("Memberlist").Range("B12").Resize(k, 2) = Res
End Sub
 
Đúng như bạn Vetmini có phân tích, và các bài viết trên diễn đàn về dictionary có ưu điểm về tốc độ và kiểm tra tính duy nhất (exsist) khi lọc các thành viên nên đã chọn dùng dictionary

Thêm nữa vì lý do cá nhân, mình chưa làm về mảng bao giờ nhưng mới học qua dictionay thôi ^_^

Trong lúc trả lời bạn thì suy nghĩ thử cách dùng mảng để cho mã số đơn hàng vào.

PHP:
dim Order_Array() as Variant
dim i as Long
i = 0
   
Do
   If dk2 = c.Offset(0, 1).Value Then
      Order_Array(i) = c.Offset(0, -2).Value, ""
   End If
   Set c = .FindNext(c)
   If c Is Nothing Then Exit Do
   Next i
Loop Until c.Address = firstAddress

Code khá phức tạp
Mã:
Sub xyz()
  Dim sArr(), Res(), shName, Dic As Object, iKey$, iKey2$
  Dim eRow&, eCol&, i&, n&, ik&, j&
  Dim DuAn$, cViec$
  Const sNV As Long = 100 'toi da 100 nhan vien

  shName = Array("4-9", "10-3")
  Set Dic = CreateObject("Scripting.Dictionary")
  With Sheets("Memberlist")
    .Range("B12:C1000").ClearContents
    DuAn = .Range("D4").Value
    cViec = .Range("D6").Value
    If DuAn = Empty Or cViec = Empty Then Exit Sub
  End With
  With Sheets("Order_List")
    sArr = .Range("A9:D370").Value
    For i = 1 To UBound(sArr)
      If sArr(i, 3) = DuAn And sArr(i, 4) = cViec Then
        iKey = sArr(i, 1)
        Dic.Item(iKey) = k
        k = k + sNV
      End If
    Next i
  End With
  ReDim Res(1 To k, 1 To 2)
  For n = 0 To UBound(shName)
    With Sheets(shName(n))
      eRow = .Range("C" & Rows.Count).End(xlUp).Row
      eCol = .Cells(4, 16000).End(xlToLeft).Column
      sArr = .Range("B5", .Cells(eRow, eCol)).Value
      For i = 1 To UBound(sArr)
        For j = 6 To UBound(sArr, 2)
          iKey = sArr(i, j)
          If Dic.exists(iKey) Then
            iKey2 = sArr(i, 1) & "#" & iKey
            If Dic.exists(iKey2) = False Then
              Dic.Add iKey2, ""
              ik = Dic.Item(iKey) + 1
              Dic.Item(iKey) = ik
              Res(ik, 1) = iKey
              Res(ik, 2) = sArr(i, 2)
            End If
          End If
        Next j
      Next i
    End With
  Next n
  k = 0
  For i = 1 To UBound(Res)
    If Res(i, 1) <> Empty Then
      k = k + 1
      Res(k, 1) = Res(i, 1)
      Res(k, 2) = Res(i, 2)
    End If
  Next i
  If k Then Sheets("Memberlist").Range("B12").Resize(k, 2) = Res
End Sub
Cám ơn bạn. Mình sẽ kiểm tra và hỏi lại nếu có chỗ nào không hiểu
 
Lần chỉnh sửa cuối:
Cám ơn bạn HieuCD rất rất nhiều. Code của bạn chạy rất tốt. Tuyệt vời.

Tuy trong code bạn có ghi là số nhân viên tối đa là 100 người, nhưng có vẻ trên 100 người vẫn không sao (đây là giới hạn số dòng trong memberlist thì phải). Thêm nữa giữa hai sheet 4-9 và sheet 10-3 thì dù số thành viên khác nhau, có thay đổi thì code của bạn vẫn đúng :)

Nếu được thì bạn cho hỏi là có giữ lại được phương thức Find để tìm và cho vào mảng hoặc dic cũng được. Tại vì mình muốn tên những dự án tương tự nhau vẫn được tìm đây. Ví dụ: khi tìm Project A vẫn lấy ra được các đơn hàng có tên dự án là "Project A" hoặc "Project A Edition".
 
Lần chỉnh sửa cuối:
Cám ơn bạn HieuCD rất rất nhiều. Code của bạn chạy rất tốt. Tuyệt vời.

Tuy trong code bạn có ghi là số nhân viên tối đa là 100 người, nhưng có vẻ trên 100 người vẫn không sao (đây là giới hạn số dòng trong memberlist thì phải). Thêm nữa giữa hai sheet 4-9 và sheet 10-3 thì dù số thành viên khác nhau, có thay đổi thì code của bạn vẫn đúng :)

Nếu được thì bạn cho hỏi là có giữ lại được phương thức Find để tìm và cho vào mảng hoặc dic cũng được. Tại vì mình muốn tên những dự án tương tự nhau vẫn được tìm đây. Ví dụ: khi tìm Project A vẫn lấy ra được các đơn hàng có tên dự án là "Project A" hoặc "Project A Edition".
100 người là giới hạn số nhân viên tham gia vào 1 Order No, nếu vượt phải tăng lại số nầy
Mình không thích dùng phương thức Find vì tốc độ chận và cách sử dụng khá rối
Mã:
Option Compare Text
Sub xyz()
  Dim sArr(), Res(), shName, Dic As Object, iKey$, iKey2$
  Dim eRow&, eCol&, i&, n&, ik&, j&
  Dim DuAn$, cViec$
  Const sNV As Long = 100 'toi da 100 nhan vien
 
  shName = Array("4-9", "10-3")
  Set Dic = CreateObject("Scripting.Dictionary")
  With Sheets("Memberlist")
    .Range("B12:C1000").ClearContents
    DuAn = .Range("D4").Value
    cViec = .Range("D6").Value
    If DuAn = Empty Or cViec = Empty Then Exit Sub
  End With
  With Sheets("Order_List")
    sArr = .Range("A9:D370").Value
    For i = 1 To UBound(sArr)
      If sArr(i, 3) Like DuAn & "*" And sArr(i, 4) = cViec Then
        iKey = sArr(i, 1)
        Dic.Item(iKey) = k
        k = k + sNV
      End If
    Next i
  End With
  ReDim Res(1 To k, 1 To 2)
  For n = 0 To UBound(shName)
    With Sheets(shName(n))
      eRow = .Range("C" & Rows.Count).End(xlUp).Row
      eCol = .Cells(4, 16000).End(xlToLeft).Column
      sArr = .Range("B5", .Cells(eRow, eCol)).Value
      For i = 1 To UBound(sArr)
        For j = 6 To UBound(sArr, 2)
          iKey = sArr(i, j)
          If Dic.exists(iKey) Then
            iKey2 = sArr(i, 1) & "#" & iKey
            If Dic.exists(iKey2) = False Then
              Dic.Add iKey2, ""
              ik = Dic.Item(iKey) + 1
              Dic.Item(iKey) = ik
              Res(ik, 1) = iKey
              Res(ik, 2) = sArr(i, 2)
            End If
          End If
        Next j
      Next i
    End With
  Next n
  k = 0
  For i = 1 To UBound(Res)
    If Res(i, 1) <> Empty Then
      k = k + 1
      Res(k, 1) = Res(i, 1)
      Res(k, 2) = Res(i, 2)
    End If
  Next i
  If k Then Sheets("Memberlist").Range("B12").Resize(k, 2) = Res
End Sub
 
Cám ơn bạn HieuCD rất nhiều. Bạn cho hỏi thêm những câu hỏi rất cơ bản về VBA.
Có điểm gì khác nhau trong việc thêm thành phần của dic theo như hai cách này

Dic.Add iKey2, ""

Dic.Item(iKey) = k

Và có điều kiện “If k then” thì có nghĩa là k tồn tại thì thực hiện lệnh tiếp theo đúng ko?
 
Cám ơn bạn HieuCD rất nhiều. Bạn cho hỏi thêm những câu hỏi rất cơ bản về VBA.
Có điểm gì khác nhau trong việc thêm thành phần của dic theo như hai cách này

Dic.Add iKey2, ""

Dic.Item(iKey) = k

Và có điều kiện “If k then” thì có nghĩa là k tồn tại thì thực hiện lệnh tiếp theo đúng ko?
Dic.Add iKey2, "" : Mục đích là loại trùng không quan tâm đến giá trị item, nên gán item="" cho gọn, nếu thích thì bạn gán gì cũng được
Dic.Item(iKey) = k: Ngoài mục đích loại trùng còn ghi nhận thứ tự dòng kết quả k, các lệnh sau sẽ lấy thứ tự dòng kết quả để xử lý tiếp
ik = Dic.Item(iKey) + 1

“If k then” là viết tắt của “If k>0 then” với k là số dòng kết quả, k>0 là có mảng kết quả mới gán kết quả lên sheet
 
Cám ơn bạn HieuCD rất nhiều. Bạn cho hỏi thêm những câu hỏi rất cơ bản về VBA.
Có điểm gì khác nhau trong việc thêm thành phần của dic theo như hai cách này

Dic.Add iKey2, ""

Dic.Item(iKey) = k

Và có điều kiện “If k then” thì có nghĩa là k tồn tại thì thực hiện lệnh tiếp theo đúng ko?
trong bất bộ Office nào cũng có 1 mớ sách hay ... nếu thật sự đam mê nó thì sẻ lục hết nó ra có nhiều thứ rất hay và hơn thế nữa
Untitled.png
 
Các bạn cho hỏi thêm về cách cho vào Validation.
Mình đã thêm một đoạn code như sau nhưng lại bị báo lỗi.
Mong các cao thủ gỡ rối giúp.
(Thông báo lỗi là key đã được sử dụng rồi)

PHP:
  With Sheets("Order_List").Range("C9:D370")
    sArr = .Value
    For i = 1 To UBound(sArr)
        iKey3 = sArr(i, 2)
        If Dic.exists(iKey3) = False Then
            Dic.Add iKey3, ""
        End If
    Next i
  End With

  With Sheets("Memberlist").Range("D6").Validation
    .Delete
    .Add xlValidateList, , , Join(Dic.keys, ",")
  End With
 
Các bạn cho hỏi thêm về cách cho vào Validation.
Mình đã thêm một đoạn code như sau nhưng lại bị báo lỗi.
Mong các cao thủ gỡ rối giúp.
(Thông báo lỗi là key đã được sử dụng rồi)

Thêm lệnh xóa Dic
Mã:
  dic.removeall
  With Sheets("Order_List").Range("C9:D370")
    sArr = .Value
    For i = 1 To UBound(sArr)
        iKey3 = sArr(i, 2)
        If Dic.exists(iKey3) = False Then
            Dic.Add iKey3, ""
        End If
    Next i
  End With

  With Sheets("Memberlist").Range("D6").Validation
    .Delete
    .Add xlValidateList, , , Join(Dic.keys, ",")
  End With
 
Cám ơn bạn HieuCD.
Đã sửa lại code thì điều kiện không đúng khiến key của Dic bị trùng lặp.

PHP:
  Dic.RemoveAll
  With Sheets("Order_list").Range("C9:D370")
    sArr = .Value
    For i = 1 To UBound(sArr)
        iKey3 = sArr(i, 2)
        If cViec = sArr(i, 1) And Dic.exists(iKey3) = False Then
            Dic.Add iKey3, ""
        End If
    Next i
  End With

  With Sheets("Memberlist").Range("D6").Validation
    .Delete
    .Add xlValidateList, , , Join(Dic.keys, ",")
  End With

Tuy nhiên lại bị lỗi tiếp theo ở dòng này.
Lỗi thông báo Application Define or Object Define Error.

.Add xlValidateList, , , Join(Dic.keys, ",")

Không biết vì sao bị lỗi nữa :(
 
Web KT

Bài viết mới nhất

Back
Top Bottom