Chuyên đề Bài tập VBA

Liên hệ QC

SA_DQ

/(hông là gì!
Thành viên danh dự
Tham gia
8/6/06
Bài viết
14,343
Được thích
22,404
Nghề nghiệp
Nuôi ba ba & trùn quế
Bài I: Chuyển dữ liệu từ 1 bảng tổng hợp
Số liệu ban đầu như sau:
| A | B 1 |Project1|Item01, Item03, Item08, Item09
2 |Project2|Item10, Item30, Item80, Item90
(Bảng 1)

Giờ muốn có 1 macro để chuyển bảng dữ liệu này thành bảng sau:
|D | E 1 |Project1|Item01
2 |Project1|Item03
3 |Project1|Item08
4 |Project1|Item09
5| Project2|Item10
. . .|. . .
8 |Project2|Item90

(Bảng 2)
Bài II: Hãy giúp tôi chuyển dữ liệu từ bảng 2 thành bảng 1
(húc Mừng Xuân Mới!
 
Chỉnh sửa lần cuối bởi điều hành viên:
Xin fép bổ sung vài dòng lệnh thêm đẹp cửa nhà đón xuân!

NMHung49;373081= Cuối năm rồi, Chúc cả đại gia đình Giải Pháp Excel năm mới vui vẻ tràn đầy hạnh phúc!!!!!!!

PHP:
Option Explicit
Sub ChaCon()
 Dim Dic As Object, iRow As Long, SArr As Variant
 Dim RArr As Variant, i As Long, KqArr As Variant
1 Const Và As String = " & ":         Const DF As String = ", "

Set Dic = CreateObject("Scripting.Dictionary")
SArr = Range("b2:c14").Value
ReDim RArr(1 To UBound(SArr, 1), 1 To UBound(SArr, 2))
 For iRow = 1 To UBound(SArr, 1)
   If Not Dic.Exists(SArr(iRow, 1)) Then
         i = i + 1
         Dic.Add SArr(iRow, 1), i
         RArr(i, 1) = SArr(iRow, 1)
         RArr(i, 2) = SArr(iRow, 2)
   Else
5     If InStr(RArr(Dic.Item(SArr(iRow, 1)), 2), Và) Then _
         RArr(Dic.Item(SArr(iRow, 1)), 2) = Replace(RArr(Dic.Item(SArr(iRow, 1)), 2), Và, DF)
      RArr(Dic.Item(SArr(iRow, 1)), 2) = RArr(Dic.Item(SArr(iRow, 1)), 2) & Và & SArr(iRow, 2)
   End If
 Next iRow
 Range("E2").Resize(i, 2).Value = RArr
9 [e1].Value = [b1].Value:                 [F1].Value = "Các " & LCase([c1])
End Sub
 
Upvote 0
Cuối năm rồi tham gia 1 bài coi kết thúc năm cũ mong năm sau mình sẽ tiếp bộ hơn, Chúc cả đại gia đình Giải Pháp Excel năm mới vui vẻ tràn đầy hạnh phúc!!!!!!!
PHP:
Sub chacon()
Dim dic As Object, iRow As Long, SourceArr As Variant
Dim RArr As Variant, i As Long
Set dic = CreateObject("Scripting.Dictionary")
SourceArr = Range("b2:c14").Value
ReDim RArr(1 To UBound(SourceArr, 1), 1 To UBound(SourceArr, 2))

    For iRow = 1 To UBound(SourceArr, 1)

        If Not dic.Exists(SourceArr(iRow, 1)) Then
            i = i + 1
            
            dic.Add SourceArr(iRow, 1), i
            RArr(i, 1) = SourceArr(iRow, 1)
            RArr(i, 2) = SourceArr(iRow, 2)
           
        Else
        
            RArr(dic.Item(SourceArr(iRow, 1)), 2) = RArr(dic.Item(SourceArr(iRow, 1)), 2) & ", " & SourceArr(iRow, 2)
        
        End If
        
    Next iRow

Range("d12").Resize(i, 2).Value = RArr
End Sub
Thiếu 1 chổ quan trọng: Kiểm tra dữ liệu có rổng hay không?
Thí nghiệm thế này:
- Xóa dòng 4 và chạy code
- Xóa C2, C4 và C6 rồi chạy code
---------------
Kinh nghiệm của tôi khi làm việc với mảng:
- Đừng bao giờ dùng End(xlUp) để xác định cell cuối cùng ---> Nó sẽ sai nếu bảng tính có dùng AutoFilter ---> Cứ khai báo thừa ra cũng chẳng việc gì
- Nếu có thể được thì đừng bao giờ dùng các hàm của Excel cũng như các phép nối chuổi trong mảng ---> Nó là nguyên nhân gây chậm quá trình tính toán
- Sau khi thí nghiệm thành công cho mọi trường hợp, nên đưa dòng On Error Resume Next lên đầu code, vì dù có thí nghiệm kỹ đến đâu cũng sẽ có những sai sót không lường hết (đương nhiên, trong quá trình thí nghiệm ta đừng cho bẫy lỗi vào để còn biết lỗi phát sinh ở đâu)
vân... vân... và vân... vân...
Tóm lại: Thí nghiệm thật kỹ!
 
Upvote 0
Bài tập cho những ai đã biết sử dụng Dic

Đầu năm khai bút GPE, không biết viết gì, buồn buồn ra bài tập mảng và Dic (khà khà)

Có dữ liệu như sau:

TT|ĐƠN VỊ|Nhan vien QL KH|Mã KH cá nhân|Mã KH Doanh nghiệp|Tên KHÁCH HÀNG|DOANH THU|
1,​
|Tổ 1|Nhân viên 001|A001| |KH 001|
40.000,​
|
2,​
|Tổ 1|Nhân viên 001|A003| |KH 003|
20.000,​
|
3,​
|Tổ 1|Nhân viên 001|A004| |KH 004|
40.000,​
|
4,​
|Tổ 2|Nhân viên 002| |A002|KH 002|
6.000,​
|
5,​
|Tổ 2|Nhân viên 002|A005| |KH 005|
10.000,​
|
6,​
|Tổ 1|Nhân viên 001|A006| |KH 006|
10.000,​
|
7,​
|Tổ 2|Nhân viên 002| |A002|KH 002|
11.000,​
|
8,​
|Tổ 1|Nhân viên 001|A003| |KH 003|
25.000,​
|
9,​
|Tổ 1|Nhân viên 001|A007| |KH 007|
60.000,​
|

Giả định:
- Một bộ phận nhiều nhân viên
- Một nhân viên phụ trtách nhiều khách hàng
- Một khách hàng chỉ do 1 nhân viên phụ trách.
- Dữ liệu nhập đúng và sort theo thời gian.

Câu 1:

Tổng hợp doanh thu của từng nhân viên theo mẫu:

ĐƠN VỊ​
|
NV.QHKH​
|
KH cá nhân​
|
KH Doanh nghiệp​
|
Tổ 1|Nhân viên 001|
195.000,​
| |
Tổ 2|Nhân viên 002|
160.000,​
|
17.000,​
|
Tổ 2|Nhân viên 003|
64.000,​
|
78.200,​
|
Tổ 3|Nhân viên 005| |
55.000,​
|
Tổ 3|Nhân viên 004|
50.600,​
|
13.600,​
|

Câu 2:

Tổng hợp doanh thu theo từng nhân viên, đồng thời đếm số lượng khách hàng có phát sinh doanh thu, theo mẫu:

ĐƠN VỊ​
|
NV.QHKH​
|
SL KH cá nhân​
|
SL KH Doanh nghiệp​
|
Dthu KH cá nhân​
|
Dthu KH Doanh nghiệp​
|
Tổ 1|Nhân viên 001|
5,​
| |
195.000,​
| |
Tổ 2|Nhân viên 002|
1,​
|
1,​
|
160.000,​
|
17.000,​
|
Tổ 2|Nhân viên 003|
1,​
|
1,​
|
64.000,​
|
78.200,​
|
Tổ 3|Nhân viên 005| |
2,​
| |
55.000,​
|
Tổ 3|Nhân viên 004|
1,​
|
1,​
|
50.600,​
|
13.600,​
|

Chú ý: tổng số 5 nhân viên, bán hàng 20 lượt, nhưng đếm số lượng khách hàng là 13, chia ra 2 loại.

Yêu cầu: Dùng mảng và Dic, không giới hạn thành viên tham gia.
 

File đính kèm

  • BaitapDic.xls
    41 KB · Đọc: 83
Lần chỉnh sửa cuối:
Upvote 0
Bài tập ế
11 lần tải, mấy chục lần đọc, mà không thấy phản hồi.

Câu 1 dễ òm à, còn câu 2 khi nào buộc chỉ dùng 1 vòng lặp mới khó thôi.
 
Upvote 0
Bài tập ế
11 lần tải, mấy chục lần đọc, mà không thấy phản hồi.

Câu 1 dễ òm à, còn câu 2 khi nào buộc chỉ dùng 1 vòng lặp mới khó thôi.
Dạ bài này em xin đóng góp 1 đoạn code, vì em không thấy các Anh, Chị tham gia
Câu 1
PHP:
Sub Cau1()
Dim Dic1 As Object, iRow As Long, i As Long
Dim Arr() As Variant, TmpArr As Variant
With Sheets("Cau1")
 .Range("E4:H10").ClearContents
  Set Dic1 = CreateObject("Scripting.Dictionary")
    TmpArr = Sheet1.Range("b2:g21").Value
    ReDim Arr(1 To UBound(TmpArr, 1), 1 To 6)
    For iRow = 1 To UBound(TmpArr, 1)
        If Not IsEmpty(TmpArr(iRow, 2)) And Not Dic1.exists(TmpArr(iRow, 2)) Then
            i = i + 1
             Dic1.Add TmpArr(iRow, 2), i
             Arr(i, 1) = TmpArr(iRow, 1)
             Arr(i, 2) = TmpArr(iRow, 2)
            If TmpArr(iRow, 3) <> "" Then
                Arr(i, 3) = TmpArr(iRow, 6)
            Else
                Arr(i, 4) = TmpArr(iRow, 6)
            End If
        Else
            If TmpArr(iRow, 3) <> "" Then
                Arr(Dic1.Item(TmpArr(iRow, 2)), 3) = Arr(Dic1.Item(TmpArr(iRow, 2)), 3) + TmpArr(iRow, 6)
            Else
                
             Arr(Dic1.Item(TmpArr(iRow, 2)), 4) = Arr(Dic1.Item(TmpArr(iRow, 2)), 4) + TmpArr(iRow, 6)
                
            End If
        End If
    Next iRow
.Range("e4").Resize(i, 4).Value = Arr
End With
End Sub
Câu 2
PHP:
Sub Cau2()
Dim Dic1 As Object, Dic2 As Object, iRow As Long, i As Long, dem As Long
Dim Arr() As Variant, TmpArr As Variant, Tmp As Variant, Olddem As Long
With Sheets("Cau2")
 .Range("E21:F35").ClearContents
  Set Dic1 = CreateObject("Scripting.Dictionary")
  Set Dic2 = CreateObject("Scripting.Dictionary")
    TmpArr = Sheet1.Range("b2:g21").Value
    ReDim Arr(1 To UBound(TmpArr, 1), 1 To 6)
    dem = 0
    For iRow = 1 To UBound(TmpArr, 1)
    Tmp = TmpArr(iRow, 2) & TmpArr(iRow, 5)
    Olddem = dem
        If Not IsEmpty(Tmp) And Not Dic2.exists(Tmp) Then
                dem = dem + 1
                Dic2.Add Tmp, dem
                
        End If
        If Not IsEmpty(TmpArr(iRow, 2)) And Not Dic1.exists(TmpArr(iRow, 2)) Then
            i = i + 1
            
             Dic1.Add TmpArr(iRow, 2), i
             Arr(i, 1) = TmpArr(iRow, 1)
             Arr(i, 2) = TmpArr(iRow, 2)
            If TmpArr(iRow, 3) <> "" Then
                Arr(i, 5) = TmpArr(iRow, 6)
                Arr(i, 3) = 1
            Else
                Arr(i, 6) = TmpArr(iRow, 6)
                Arr(i, 4) = 1
            End If
        Else
            If TmpArr(iRow, 3) <> "" Then
                Arr(Dic1.Item(TmpArr(iRow, 2)), 5) = Arr(Dic1.Item(TmpArr(iRow, 2)), 5) + TmpArr(iRow, 6)
                If Olddem <> dem Then Arr(Dic1.Item(TmpArr(iRow, 2)), 3) = Arr(Dic1.Item(TmpArr(iRow, 2)), 3) + 1
            Else
                
                Arr(Dic1.Item(TmpArr(iRow, 2)), 6) = Arr(Dic1.Item(TmpArr(iRow, 2)), 6) + TmpArr(iRow, 6)
                If Olddem <> dem Then Arr(Dic1.Item(TmpArr(iRow, 2)), 4) = Arr(Dic1.Item(TmpArr(iRow, 2)), 4) + 1
            End If
        End If
    Next iRow
.Range("a21").Resize(i, 6).Value = Arr
End With
End Sub
Em xin cảm ơn Thầy Mỹ đã chỉ dẫn. Thanks Thầy thật nhiều, Mong các bạn và Anh, Chị góp ý thêm /-*+/
 

File đính kèm

  • BaitapDic.xls
    58 KB · Đọc: 64
Upvote 0
Cả 2 bài đều chuẩn. Cứ như nmhung đọc bài giải trước vậy? Nhất là câu 2, dùng 2 Dic và dùng 2 biến đếm cũ và mới.

Nhưng sao đặt tên biến nghe cứ kỳ kỳ thế nào ấy? OldDem? tiếng nước nào đây?

nmhung cho hỏi nhỏ 1 câu: Có câu 1 rồi, làm câu 2 mất bao nhiêu lâu?
 
Lần chỉnh sửa cuối:
Upvote 0
Cả 2 bài đều chuẩn. Cứ như nmhung đọc bài giải trước vậy? Nhất là câu 2, dùng 2 Dic và dùng 2 biến đếm cũ và mới.

Nhưng sao đặt tên biến nghe cứ kỳ kỳ thế nào ấy? OldDem? tiếng nước nào đây?

nmhung cho hỏi nhỏ 1 câu: Có câu 1 rồi, làm câu 2 mất bao nhiêu lâu?
Câu 2 em làm đâu có ra đâu sư phụ chỉ đó mà còn hỏi nữa ta!!!!, lâu lâu đặt tên biến cho nó lạ đó mà he...he... chứ đâu có tiếng nước nào đâu Anh với Việt pha lộn he...he...
 
Upvote 0
Bài tập ế
11 lần tải, mấy chục lần đọc, mà không thấy phản hồi.

Câu 1 dễ òm à, còn câu 2 khi nào buộc chỉ dùng 1 vòng lặp mới khó thôi.
Ẹc... Ẹc... đúng là.. quá ế luôn
Sư phụ đã nói "CHẤP HẾT" rồi
không giới hạn thành viên tham gia.
vậy mà chẳng "ma" nào tham gia
Tái bút: khi nào hổng ai làm thì em sẽ... Ẹc... Ẹc... nhưng mà nói thiệt, em cũng bắt đầu thấy nhàm chán với mấy bài dạng này rồi
__--__
 
Upvote 0
À nhớ ra rồi, đề bài này lấy từ file thực của Hùng mà mình đã làm trước đó.
Làm xong thấy hay hay về mặt suy luận logic nên để nguyên cấu trúc, đổi tên, thêm dữ liệu giả lập để thành bài tập.

Ẹc ẹc, vậy là tham gia giải bài đồng nghĩa với ăn gian đó nha.
 
Upvote 0
Ẹc... Ẹc... đúng là.. quá ế luôn

Tái bút: khi nào hổng ai làm thì em sẽ... Ẹc... Ẹc... nhưng mà nói thiệt, em cũng bắt đầu thấy nhàm chán với mấy bài dạng này rồi

Dùng Dic và mảng là dùng công cụ để thực hiện thuật toán (có thể dùng công cụ khác).
Sử dụng công cụ khéo hay không đó là Thủ thuật, thậm chi xảo thuật.

Trong khi đó, học lập trình thì quan trọng ở chỗ suy luận tìm ra thuật toán đúng.

Ai đó có thể thấy nhàm chán với công cụ này và tìm công cụ khác ngon hơn, nhanh hơn, ... Nhưng khi đã chán suy luận rồi thì tiêu đó nha. Mấy bài tập của mình đưa ra, hoặc những cách giải, toàn là những thí dụ về suy luận: Kể cả suy luận thông thường và suy luận khác thường.

Có thể Ndu sẽ có cách giải khác (thuật toán khác) hay hơn, vậy cứ đưa lên, lỡ ế rồi. (nmhung không tính, vì đó là bài giải của lão chết tiệt)
 
Upvote 0
Ai đó có thể thấy nhàm chán với công cụ này và tìm công cụ khác ngon hơn, nhanh hơn, ... Nhưng khi đã chán suy luận rồi thì tiêu đó nha. Mấy bài tập của mình đưa ra, hoặc những cách giải, toàn là những thí dụ về suy luận: Kể cả suy luận thông thường và suy luận khác thường.
Ah... không!
Ý em nói "chán" là "chán" mấy cái bài LỌC VÀ TỔNG HỢP DỮ LIỆU THEO ĐIỀU KIỆN ấy mà (chứ còn Dic và Array thì luôn là ưu tiên hàng đâu của em khi xử lý dữ liệu)
Ẹc... Ẹc...
 
Upvote 0
Dùng Dic và mảng là dùng công cụ để thực hiện thuật toán (có thể dùng công cụ khác).
Sử dụng công cụ khéo hay không đó là Thủ thuật, thậm chi xảo thuật.

Trong khi đó, học lập trình thì quan trọng ở chỗ suy luận tìm ra thuật toán đúng.

Ai đó có thể thấy nhàm chán với công cụ này và tìm công cụ khác ngon hơn, nhanh hơn, ... Nhưng khi đã chán suy luận rồi thì tiêu đó nha. Mấy bài tập của mình đưa ra, hoặc những cách giải, toàn là những thí dụ về suy luận: Kể cả suy luận thông thường và suy luận khác thường.

Có thể Ndu sẽ có cách giải khác (thuật toán khác) hay hơn, vậy cứ đưa lên, lỡ ế rồi. (nmhung không tính, vì đó là bài giải của lão chết tiệt)
Em thi dùng cách khác một chút, còn nhanh hơn hay chậm hơn thì chưa biết.
Theo em thì em hiểu cách của em hơn.
PHP:
Sub Cau2Dic()
Dim endR&, i&, s&, nR&
Dim Tmp1$, Tmp2$
Dim Arr(), ArrKq()
Dim Dic1 As Object, Dic2 As Object
Set Dic1 = CreateObject("Scripting.Dictionary")
Set Dic2 = CreateObject("Scripting.Dictionary")
With Sheets("Data")
  endR = .Cells(65000, 1).End(3).Row
  Arr = .Range("B2:G" & endR).Value
End With
ReDim ArrKq(1 To UBound(Arr), 1 To 6)
For i = 1 To UBound(Arr)
  If Len(Arr(i, 1)) > 0 Then
    If Len(Arr(i, 2)) > 0 Then
      Tmp1 = Arr(i, 1) & Arr(i, 2)
      If Not Dic1.Exists(Tmp1) Then
        s = s + 1
        Dic1.Add Tmp1, s
        ArrKq(s, 1) = Arr(i, 1)
        ArrKq(s, 2) = Arr(i, 2)
      End If
      nR = Dic1.Item(Tmp1)
      Select Case Len(Arr(i, 3))
        Case Is > 0
          Tmp2 = Tmp1 & Arr(i, 3) & Arr(i, 4)
          If Not Dic2.Exists(Tmp2) Then
            Dic2.Add Tmp2, Nothing
            ArrKq(nR, 3) = ArrKq(nR, 3) + 1
          End If
          ArrKq(nR, 5) = ArrKq(nR, 5) + Arr(i, 6)
        Case Else
          Tmp2 = Tmp1 & vbBack & Arr(i, 4)
          If Not Dic2.Exists(Tmp2) Then
            Dic2.Add Tmp2, Nothing
            ArrKq(nR, 4) = ArrKq(nR, 4) + 1
          End If
          ArrKq(nR, 6) = ArrKq(nR, 6) + Arr(i, 6)
      End Select
    End If
  End If
Next i
If s > 0 Then
  With Sheets("Cau2")
    With .[A4]
      .Resize(1000, 6).ClearContents
      .Resize(s, 6) = ArrKq
    End With
  End With
End If
Erase Arr(), ArrKq()
Set Dic1 = Nothing: Set Dic2 = Nothing
End Sub
 
Upvote 0
Bài giải gốc của câu 2: (viết gọn hơn nmhung, dù sao thì Hùng đã hiểu và làm lại đúng thuật toán và ứng dụng Dic đúng cách)

PHP:
Sub BaiTapVBA()
    Dim Dic1, Dic2, sArr, rArr, k, OldK, i, s, nR, EndR
    OldK = 0: k = 0
    Set Dic1 = CreateObject("Scripting.Dictionary")
    Set Dic2 = CreateObject("Scripting.Dictionary")
    With Sheet1
        If .AutoFilterMode = True Then .AutoFilterMode = False
        EndR = .[a65000].End(xlUp).Row
        sArr = .[B2].Resize(EndR - 1, 6).Value
    End With
    ReDim rArr(1 To EndR - 1, 1 To 6)
        For i = 1 To UBound(sArr)
            OldK = k
            If Not Dic2.exists(sArr(i, 3) & sArr(i, 4)) Then
                k = k + 1
                Dic2.Add sArr(i, 3) & sArr(i, 4), k
            End If
            If Not Dic1.exists(sArr(i, 1) & sArr(i, 2)) Then
                s = s + 1
                Dic1.Add sArr(i, 1) & sArr(i, 2), s
                rArr(s, 1) = sArr(i, 1): rArr(s, 2) = sArr(i, 2)
                If sArr(i, 3) <> "" Then
                    rArr(s, 5) = sArr(i, 6)
                    rArr(s, 3) = 1
                Else
                    rArr(s, 6) = sArr(i, 6)
                    rArr(s, 4) = 1
                End If
            Else
                nR = Dic1.Item(sArr(i, 1) & sArr(i, 2))
                If sArr(i, 3) <> "" Then
                    rArr(nR, 5) = rArr(nR, 5) + sArr(i, 6)
                    If OldK <> k Then rArr(nR, 3) = rArr(nR, 3) + 1
                Else
                    rArr(nR, 6) = rArr(nR, 6) + sArr(i, 6)
                    If OldK <> k Then rArr(nR, 4) = rArr(nR, 4) + 1
                End If
             End If
        Next
    Sheet2.[A4].Resize(s, 6) = rArr

End Sub

Nói chung code Thu Nghi và code trên cùng thuật toán:

- Tạo 2 danh sách duy nhất của nhân viên và của khách hàng
- Nếu gặp nhân viên mới, add vào danh sách và doanh thu bằng doanh thu dòng đó
- Nếu gặp mã KH mới, tăng thêm 1 và gán vào cột đếm tương ứng.
- Nếu gặp mã nhân viên đã có, cộng doanh thu vào tổng doanh thu tương ứng
- Nếu gặp mã KH đã có, không cộng thêm.

Về công cụ:
- Đều dùng 2 Dic, mỗi Dic là 1 danh sách duy nhất cho nhân viên và khách hàng

Sự khác biệt 2 code nằm ở thủ thuật sử dụng Dic và sắp xếp code:

- Code của ptm0412, dùng 1 biến đếm danh sách KH để đánh dấu, khi biến này thay đổi (Oldk < k) nghĩa là có thêm 1 KH mới chưa có trong danh sách, thì cộng thêm 1, không thì thôi. Do đó, code Add cho Dic2 chỉ viết 1 lần ở đầu vòng lặp

- Code của ThuNghi, không dựa vào biến đếm, nên câu lệnh Add cho Dic2 phải viết 2 lần trong cấu trúc If, dù chỉ chạy 1 ltrong 2 lần đó.
 
Upvote 0
Bài giải gốc của câu 2: (viết gọn hơn nmhung, dù sao thì Hùng đã hiểu và làm lại đúng thuật toán và ứng dụng Dic đúng cách)

Mã:
Sub BaiTapVBA()
    Dim Dic1, Dic2, sArr, rArr, k, OldK, i, s, nR, EndR
    OldK = 0: k = 0
    Set Dic1 = CreateObject("Scripting.Dictionary")
    Set Dic2 = CreateObject("Scripting.Dictionary")
    With Sheet1
       [B] If .AutoFilterMode = True Then .AutoFilterMode = False[/B]
        EndR = .[a65000].End(xlUp).Row
        sArr = .[B2].Resize(EndR - 1, 6).Value
    End With

.........................................
End Sub


Chotter:

Nếu không muốn nó có Filter, Sư phụ cứ cho nó False chứ cần gì phải IF! ẹc ẹc ...
Mã:
    With Sheet1
       [B][COLOR=#ff0000] .AutoFilterMode = False[/COLOR][/B]
        EndR = .[a65000].End(xlUp).Row
        sArr = .[B2].Resize(EndR - 1, 6).Value
    End With
 
Upvote 0
Những câu lệnh dạng này tập thói quen cẩn thận vẫn hơn. Gặp trường hợp khác biết đâu không được như vậy.
 
Upvote 0
Nếu thay thế hàm Vlookup trong Excel bằng VBA thì cách làm thế nào

Bài toán của em có yêu cầu là điền cột đơn giá tại Sheet1 vào Sheet 2. Nếu bài này làm bằng Excel đơn thuần dùng hàm Vlookup là đơn giản nhất.

Bản thân em muốn tìm thêm cách giải bài này bằng VBA thì cách làm ra sao, em nghĩ mãi chưa ra, xin được chỉ dùm em./.
 

File đính kèm

  • tim kiem.xlsx
    10.8 KB · Đọc: 33
Upvote 0
Bài toán của em có yêu cầu là điền cột đơn giá tại Sheet1 vào Sheet 2. Nếu bài này làm bằng Excel đơn thuần dùng hàm Vlookup là đơn giản nhất.

Bản thân em muốn tìm thêm cách giải bài này bằng VBA thì cách làm ra sao, em nghĩ mãi chưa ra, xin được chỉ dùm em./.
Code thế này xem
PHP:
Public Dic As Object
Sub DienDG()
  Dim pList, sArray, tmp1 As String, tmp2 As Double, i As Long, j As Long
  On Error Resume Next
  If Dic Is Nothing Then
    Set Dic = CreateObject("Scripting.Dictionary")
    pList = Sheet1.Range("A2:B1000").Value
    For i = 1 To UBound(pList, 1)
      If pList(i, 1) <> "" Then
        tmp1 = CStr(pList(i, 1))
        tmp2 = CDbl(pList(i, 2))
        If Not Dic.Exists(tmp1) Then Dic.Add tmp1, tmp2
      End If
    Next
  End If
  With Sheet2.Range("A2:E10000")
    sArray = .Value
    For i = 1 To UBound(sArray, 1)
      If sArray(i, 1) <> "" Then
        sArray(i, 4) = Dic.Item(CStr(sArray(i, 2)))
        sArray(i, 5) = sArray(i, 3) * sArray(i, 4)
      End If
    Next
    .Value = sArray
  End With
End Sub
Lưu ý: Code chỉ đúng với cấu trúc dữ liệu như trong file của bạn. Nếu dữ liệu thật có khác hơn, bạn phải tự mình chỉnh lấy
 
Upvote 0
Mong thày, các anh chị giúp em hiểu thêm về lý thuyết Scripting.Dictionary, cái này em chưa được rõ cho lắm.
 
Upvote 0
Bài giải gốc của câu 2: (viết gọn hơn nmhung, dù sao thì Hùng đã hiểu và làm lại đúng thuật toán và ứng dụng Dic đúng cách)

Nói chung code Thu Nghi và code trên cùng thuật toán:

- Tạo 2 danh sách duy nhất của nhân viên và của khách hàng
- Nếu gặp nhân viên mới, add vào danh sách và doanh thu bằng doanh thu dòng đó
- Nếu gặp mã KH mới, tăng thêm 1 và gán vào cột đếm tương ứng.
- Nếu gặp mã nhân viên đã có, cộng doanh thu vào tổng doanh thu tương ứng
- Nếu gặp mã KH đã có, không cộng thêm.

Về công cụ:
- Đều dùng 2 Dic, mỗi Dic là 1 danh sách duy nhất cho nhân viên và khách hàng
...
Em thử dùng 1 Dic và dùng thêm Instr thấy với dữ liệu # 60.000 records thì thấy nhanh hơn dùng 2 Dic
PHP:
Sub Cau2Dic1()
Dim T As Double
T = Timer
Dim endR&, i&, s&, nR&
Dim Tmp1$
Dim Arr(), ArrKq()
Dim Dic1 As Object
Set Dic1 = CreateObject("Scripting.Dictionary")
With Sheets("Data")
  endR = .Cells(65000, 1).End(3).Row
  Arr = .Range("B2:G" & endR).Value
End With
ReDim ArrKq(1 To UBound(Arr), 1 To 8)
For i = 1 To UBound(Arr)
  If Len(Arr(i, 1)) > 0 Then
    If Len(Arr(i, 2)) > 0 Then
      Tmp1 = Arr(i, 1) & Arr(i, 2)
      If Not Dic1.exists(Tmp1) Then
        s = s + 1
        Dic1.Add Tmp1, s
        ArrKq(s, 1) = Arr(i, 1)
        ArrKq(s, 2) = Arr(i, 2)
      End If
      nR = Dic1.Item(Tmp1)
      Select Case Len(Arr(i, 3))
        Case Is > 0
          If Len(ArrKq(nR, 7)) = 0 Then
            ArrKq(nR, 7) = Arr(i, 3) 'Add vao
            ArrKq(nR, 3) = ArrKq(nR, 3) + 1
            ArrKq(nR, 5) = ArrKq(nR, 5) + Arr(i, 6)
          Else
            If InStr(ArrKq(nR, 7), Arr(i, 3)) Then
              ArrKq(nR, 5) = ArrKq(nR, 5) + Arr(i, 6)
            Else
              ArrKq(nR, 7) = ArrKq(nR, 7) & Arr(i, 3) 'Add vao
              ArrKq(nR, 3) = ArrKq(nR, 3) + 1
              ArrKq(nR, 5) = ArrKq(nR, 5) + Arr(i, 6)
            End If
          End If
        Case Is = 0
          If Len(ArrKq(nR, 8)) = 0 Then
            ArrKq(nR, 8) = Arr(i, 4) 'Add vao
            ArrKq(nR, 4) = ArrKq(nR, 4) + 1
            ArrKq(nR, 6) = ArrKq(nR, 6) + Arr(i, 6)
          Else
            If InStr(ArrKq(nR, 8), Arr(i, 4)) Then
              ArrKq(nR, 6) = ArrKq(nR, 6) + Arr(i, 6)
            Else
              ArrKq(nR, 8) = ArrKq(nR, 8) & Arr(i, 4) 'Add vao
              ArrKq(nR, 4) = ArrKq(nR, 4) + 1
              ArrKq(nR, 6) = ArrKq(nR, 6) + Arr(i, 6)
            End If
          End If
      End Select
    End If
  End If
Next i
If s > 0 Then
  With Sheets("Cau2")
    With .[A4]
      .Resize(60000, 6).ClearContents
      .Resize(s, 6) = ArrKq
    End With
    .[I3] = Timer - T
  End With
End If
Erase Arr(), ArrKq()
Set Dic1 = Nothing
End Sub
 
Upvote 0
Test 60.000 dòng nhưng test với dữ liệu mẫu nào mới được?

Thuật toán dùng Instr tức là nối chuỗi và tìm trong chuỗi. Vậy phải giả lập dữ liệu dạng nối chuỗi dài. Chẳng hạn như 50 nhân viên, mỗi nhân viên quản lý 100 khách hàng (tức là nối 100 chuỗi thành 1). Chứ cũng 5 nhân viên và 13 khách hàng, mỗi nhân viên quản lý tối đa 5 KH, nối tối đa 5 chuỗi là 20 ký tự, thì làm sao thấy ưu khuyết?

Vì bản thân thuật toán nối chuỗi và Instr là chậm. Hãy xem bài của Paulstiegel số #16, đã test tốc độ trong bài tiếp theo.
 
Upvote 0
Web KT
Back
Top Bottom