Lọc dữ liệu theo nhiều điều kiện bằng mảng arr ?

Liên hệ QC

moihocvba

Thành viên thường trực
Tham gia
16/8/20
Bài viết
211
Được thích
50
Xin chào anh chị!

Bài em sắp hỏi tới đây có thể dùng macro Advance Filter cũng được , nhưng nếu dữ liệu nhiều thì code sẽ chậm và file sẽ nặng nên em xin phép hỏi anh chị cách dùng mảng để cho nhẹ ạ!

Em có bảng nhập liệu A như hình:

1598195428518.png


Sau đó em có 1 sheet để lọc ra các kết quả theo điều kiện:

1598195958260.png

Vùng điều kiện ở đây em ví dụ kết hợp 4 điều kiện, thực tế có thể hơn.

Từ ô B3 đến ô E3 ô nào không có có nghĩa là không lọc điều kiện đó (ví dụ trên là em chỉ lọc 2 điều kiện).
Nếu có cả 4 điều kiện thì dùng mảng em viết được. Nhưng nếu khuyết 1 hoặc 2 hoặc 3, 4 trong các điều kiện trên thì mình viết code sao ạ?

Đây là ví dụ, trong thực tế em làm có thể tới 7 8 điều kiện cùng lúc. Mong anh chị GPE giúp em! Cảm ơn anh chị nhiều!
 

File đính kèm

  • Trich loc bang mang.xlsm
    11.3 KB · Đọc: 25
Upvote 0
Thấy cách trình bày đề bài quen quen...

Không biết đáp án có cần thử nghiệm trong phòng thí nghiệm 3 tháng không... nhưng tạm gợi ý như vầy.

Hai cách: Dùng ADO Query, hoặc dùng mảng (array).

Nếu dùng mảng thì:
- Chép dữ liệu nguồn vào mảng.
- Xét từng dòng với 4 điều kiện: Ở đây đoán khi không nhập điều kiện thì ứng với thỏa mãn tất cả nên có thể dùng hàm Instr()
- Thỏa mãn thì lấy kết quả ghi vào mảng kết quả.
- Cuối cùng ghi mảng kết quả xuống bảng tính.
 
Upvote 0
Nếu dùng mảng thì:
- Chép dữ liệu nguồn vào mảng.
- Xét từng dòng với 4 điều kiện: Ở đây đoán khi không nhập điều kiện thì ứng với thỏa mãn tất cả nên có thể dùng hàm Instr()
- Thỏa mãn thì lấy kết quả ghi vào mảng kết quả.
- Cuối cùng ghi mảng kết quả xuống bảng tính.
Xét từng dòng với 4 điều kiện:
IIf(len(dk1)>0, arr(gì đó) = dk1, True) And IIf(len(dk2)>0, arr(gì đó) = dk2, True) And IIf(len(dk3)>0, arr(gì đó) = dk3, True) And IIf(len(dk4)>0, arr(gì đó) = dk4, True)
:p

Nhiều hơn 4 điều kiện thì dùng vòng lặp
 
Upvote 0
Wave chứ không phải wawe
Chiếc chứ không phải chiết
Điều kiện "nơi sản xuất" sao lại 2019?
---

Code bạn "làm được" đâu?
Đây là code em làm với Advance filter:
Mã:
Sub LocDuLieu()
    Sheets("Data").Range("A3:H65000").AdvancedFilter Action:=xlFilterCopy, _
        CriteriaRange:=Range("B2:E3"), CopyToRange:=Range("A6:H6"), Unique:=False
End Sub

Nếu dùng mảng mà có cả 4 điều kiện trên thì code em làm là:
Mã:
Sub LocDuLieu2()
    Dim arr(), kq(), i As Long, a As Long, lr As Long, j As Long
    lr = Sheet1.Range("A65000").End(xlUp).Row
    arr = Sheet1.Range("A4:H" & lr).Value
    ReDim kq(1 To UBound(arr), 1 To 8)
    
    For i = 1 To UBound(arr)
        If arr(i, 1) = Range("B3").Value _
        And arr(i, 2) = Range("C3").Value _
        And arr(i, 3) = Range("D3").Value _
        And arr(i, 4) = Range("E3").Value Then
            a = a + 1
            For j = 1 To 8
                kq(a, j) = arr(i, j)
            Next j
        End If
    Next i
    
Range("A7:H65000").ClearContents
On Error Resume Next ' tranh loi khi khong co ket qua
Range("A7").Resize(a, 8).Value = kq
End Sub

Còn nếu khuyết 1 trong các điều kiện thì em dùng If then else để xét lần lượt từng điều kiện cũng được nhưng như vậy là rất dài và lượm thượm.
anh chị có cách nào hay chỉ em với, Cảm ơn anh chị đã tư vấn.
 

File đính kèm

  • Trich loc bang mang.xlsm
    19.2 KB · Đọc: 18
Upvote 0
Xin chào anh chị!

Bài em sắp hỏi tới đây có thể dùng macro Advance Filter cũng được , nhưng nếu dữ liệu nhiều thì code sẽ chậm và file sẽ nặng nên em xin phép hỏi anh chị cách dùng mảng để cho nhẹ ạ!

Em có bảng nhập liệu A như hình:

View attachment 243846


Sau đó em có 1 sheet để lọc ra các kết quả theo điều kiện:

View attachment 243847

Vùng điều kiện ở đây em ví dụ kết hợp 4 điều kiện, thực tế có thể hơn.

Từ ô B3 đến ô E3 ô nào không có có nghĩa là không lọc điều kiện đó (ví dụ trên là em chỉ lọc 2 điều kiện).
Nếu có cả 4 điều kiện thì dùng mảng em viết được. Nhưng nếu khuyết 1 hoặc 2 hoặc 3, 4 trong các điều kiện trên thì mình viết code sao ạ?

Đây là ví dụ, trong thực tế em làm có thể tới 7 8 điều kiện cùng lúc. Mong anh chị GPE giúp em! Cảm ơn anh chị nhiều!
Tặng bạn file này!
Trong file có sử dụng đoạn code của anh @siwtom @batman1
Với vùng dữ liệu khoảng vài nghìn dòng, tốc độ của code tương đối ổn định phù hợp với yêu cầu của bạn.
Mã:
Option Explicit
Sub main()
'Function MyFilter2DArray(ByVal sArray As Variant, ArrCrit(), ByVal HasTitle As Boolean, Optional ByVal arg_and As Boolean = True)
    Dim sArray, ArrCrit(), Arrresult
        ArrCrit = Range("B2:E3")
        With Worksheets("Data")
            sArray = .Range("A3", .[A65536].End(3)).Resize(, 8)
        End With
        Arrresult = MyFilter2DArray(sArray, ArrCrit, True)
        If TypeName(Arrresult) = "Variant()" Then
            Range("A6:D1000").ClearContents
            Range("A6:D7").Resize(UBound(Arrresult, 1)) = Arrresult
        End If
     
End Sub

p/s : Theo kinh nghiệm của tôi, các phương thức có sẵn của Excel là tối ưu nhất rồi ( ví dụ Advanced Filter hay Remove Duplicate,...) nếu code viết trên nền VBA về tốc độ , tôi nghĩ vẫn không thể bằng được. Trừ phi bạn lập trình từ ứng dụng bên ngoài (kiểu Dll, Vb.net gì gì đấy)
 

File đính kèm

  • Trich loc bang mang (By Hungnm).xlsm
    30.7 KB · Đọc: 30
Lần chỉnh sửa cuối:
Upvote 0
Xét từng dòng với 4 điều kiện:
IIf(len(dk1)>0, arr(gì đó) = dk1, True) And IIf(len(dk2)>0, arr(gì đó) = dk2, True) And IIf(len(dk3)>0, arr(gì đó) = dk3, True) And IIf(len(dk4)>0, arr(gì đó) = dk4, True)
:p

Nhiều hơn 4 điều kiện thì dùng vòng lặp
anh cho ví dụ đoạn code lập vòng để tham khảo với anh.
 
Upvote 0
Xét từng dòng với 4 điều kiện:
IIf(len(dk1)>0, arr(gì đó) = dk1, True) And IIf(len(dk2)>0, arr(gì đó) = dk2, True) And IIf(len(dk3)>0, arr(gì đó) = dk3, True) And IIf(len(dk4)>0, arr(gì đó) = dk4, True)
:p

Nhiều hơn 4 điều kiện thì dùng vòng lặp
Anh ví dụ cho em dễ hiểu được ko anh?
Bài đã được tự động gộp:

p/s : Theo kinh nghiệm của tôi, các phương thức có sẵn của Excel là tối ưu nhất rồi ( ví dụ Advanced Filter hay Remove Duplicate,...) nếu code viết trên nền VBA về tốc độ , tôi nghĩ vẫn không thể bằng được. Trừ phi bạn lập trình từ ứng dụng bên ngoài (kiểu Dll, Vb.net gì gì đấy)
Em đã làm theo kiểu của Excel, đã thử với dữ liệu 65000 dòng, em nhận thấy tốc độ xử lý rất chậm, và file sẽ tăng dung lượng rất lớn so với dùng mảng. Vì em nghĩ dùng advance filter nó ko chỉ duyệt value mà nó còn xử lý trên range nên chậm và khi xuất ra kết quả nó cũng mang theo các thuộc tính của range nên sẽ làm file mình nặng hơn.
Bài đã được tự động gộp:

Em đã coi file của anh hungpecc1
Cho em hỏi 2 vấn đề nữa nhé:
1598235359508.png
Số thứ tự 1 2 3 4 là số thứ cột trong data, hay là cứ đánh theo thứ tự các điều kiện?

1598235406653.png
Sau khi bấm vào nút thì nó chỉ ra 4 cột, ko ra các cột sau, vậy mình phải sửa chỗ nào ạ?
 
Lần chỉnh sửa cuối:
Upvote 0
Đây là code em làm với Advance filter:
Mã:
Sub LocDuLieu()
    Sheets("Data").Range("A3:H65000").AdvancedFilter Action:=xlFilterCopy, _
        CriteriaRange:=Range("B2:E3"), CopyToRange:=Range("A6:H6"), Unique:=False
End Sub

Còn nếu khuyết 1 trong các điều kiện thì em dùng If then else để xét lần lượt từng điều kiện cũng được nhưng như vậy là rất dài và lượm thượm.
anh chị có cách nào hay chỉ em với, Cảm ơn anh chị đã tư vấn.
Tôi nghĩ kiểu nhà quê nhưng hiệu quả nhất là dùng đoạn code trên nhưng trước đó dùng code điền các giá trị điều kiện cần lọc vào vùng B2:E3 rồi chép kết quả ra nơi khác. Mở rộng ra bạn có thể lọc bao nhiêu trường cũng dễ dàng, code gọn gàng chứ không lê thê luộm thuộm.
 
Upvote 0
Anh ví dụ cho em dễ hiểu được ko anh?
Bài đã được tự động gộp:


Em đã làm theo kiểu của Excel, đã thử với dữ liệu 65000 dòng, em nhận thấy tốc độ xử lý rất chậm, và file sẽ tăng dung lượng rất lớn so với dùng mảng. Vì em nghĩ dùng advance filter nó ko chỉ duyệt value mà nó còn xử lý trên range nên chậm và khi xuất ra kết quả nó cũng mang theo các thuộc tính của range nên sẽ làm file mình nặng hơn.
Bài đã được tự động gộp:

Em đã coi file của anh hungpecc1
Cho em hỏi 2 vấn đề nữa nhé:



View attachment 243868
Sau khi bấm vào nút thì nó chỉ ra 4 cột, ko ra các cột sau, vậy mình phải sửa chỗ nào ạ?
Mã:
If TypeName(Arrresult) = "Variant()" Then
            Range("A6:H1000").ClearContents
            Range("A6:H7").Resize(UBound(Arrresult, 1)) = Arrresult
        End If
 
Upvote 0
Em đã làm theo kiểu của Excel, đã thử với dữ liệu 65000 dòng, em nhận thấy tốc độ xử lý rất chậm, và file sẽ tăng dung lượng rất lớn so với dùng mảng. Vì em nghĩ dùng advance filter nó ko chỉ duyệt value mà nó còn xử lý trên range nên chậm và khi xuất ra kết quả nó cũng mang theo các thuộc tính của range nên sẽ làm file mình nặng hơn.
PHP:
Sub FilterAdvanced() '194.316 dòng'
Dim tmr
    Application.ScreenUpdating = False
    tmr = Timer()
    Range("A3:N" & Range("A" & Rows.Count).End(xlUp).Row).AdvancedFilter xlFilterCopy, Range("P1:S2"), Range("P3:AC3"), Unique:=False
    Range("M1") = Timer() - tmr
    Application.ScreenUpdating = True
End Sub
Tôi đã thử dùng code trên với dữ liệu 194 ngàn dòng, thời gian xử lý chỉ 0,1875 giây. Bạn làm thế nào mà nhận thấy "tốc độ xử lý rất chậm"?
 
Upvote 0
Tặng bạn file này!
Trong file có sử dụng đoạn code của anh Switom
Với vùng dữ liệu khoảng vài nghìn dòng, tốc độ của code tương đối ổn định phù hợp với yêu cầu của bạn.
Tôi thử dùng thì code này chưa xét trường hợp Arrcrit() có 1 hoặc nhiều phần tử có giá trị rỗng
Nhắc bạn: nick đúng của tác giả code là @siwtom chứ không phải Switom
anh chị có cách nào hay chỉ em với, Cảm ơn anh chị đã tư vấn.
PHP:
Sub LocDuLieu2()
    Dim arr(), kq(), i As Long, a As Long, lr As Long, j As Long
    lr = Sheet1.Range("A65000").End(xlUp).Row
    arr = Sheet1.Range("A4:H" & lr).Value
    ReDim kq(1 To UBound(arr), 1 To 8)
    For i = 1 To UBound(arr)
        If _
        IIf(Len(Range("B3")) > 0, arr(i, 1) = Range("B3").Value, True) And _
        IIf(Len([C3]) > 0, arr(i, 2) = Range("C3").Value, True) And _
        IIf(Len([D3]) > 0, arr(i, 3) = Range("D3").Value, True) And _
        IIf(Len([E3]) > 0, arr(i, 4) = Range("E3").Value, True) Then
            a = a + 1
            For j = 1 To 8
                kq(a, j) = arr(i, j)
            Next j
        End If
    Next i
 
Range("A7:H65000").ClearContents
On Error Resume Next ' tranh loi khi khong co ket qua'
Range("A7").Resize(a, 8).Value = kq
End Sub
anh cho ví dụ đoạn code lập vòng để tham khảo với anh.
PHP:
Sub LocDuLieu3()
    Dim arr(), kq(), i As Long, a As Long, lr As Long, j As Long
    Dim DkTong As Boolean, Dkien()
    lr = Sheet1.Range("A65000").End(xlUp).Row
    arr = Sheet1.Range("A4:H" & lr).Value
    Dkien = Sheet2.Range("B3:E3").Value
    ReDim kq(1 To UBound(arr), 1 To 8)
    For i = 1 To UBound(arr)
        DkTong = True
        For k = 1 To UBound(Dkien, 2)
            If Len(Dkien(1, k)) > 0 Then DkTong = DkTong And (arr(i, k) = Dkien(1, k))
            If DkTong = False Then Exit For
        Next
        If DkTong = True Then
            a = a + 1
            For j = 1 To 8
                kq(a, j) = arr(i, j)
            Next j
        End If
    Next i
 
Range("A7:H65000").ClearContents
On Error Resume Next ' tranh loi khi khong co ket qua
Range("A7").Resize(a, 8).Value = kq
End Sub
Điều kiện là vùng điều kiện theo đúng thứ tự cột giống dữ liệu (như file gốc của tác giả). Nếu khác thứ tự thì xử tiếp bằng 1 mảng con khác
 
Lần chỉnh sửa cuối:
Upvote 0
Tôi đã thử dùng code trên với dữ liệu 194 ngàn dòng, thời gian xử lý chỉ 0,1875 giây. Bạn làm thế nào mà nhận thấy "tốc độ xử lý rất chậm"?
Cần phải kiểm tra kết quả có đủ số lượng dòng mong muốn hay không, vì bản thân Advanced filter (chạy bằng tay) cũng có lỗi không lấy hết dữ liệu khi vượt quá 1 số dòng nào đó.
 
Upvote 0
Đây là code em làm với Advance filter:
Mã:
Sub LocDuLieu()
    Sheets("Data").Range("A3:H65000").AdvancedFilter Action:=xlFilterCopy, _
        CriteriaRange:=Range("B2:E3"), CopyToRange:=Range("A6:H6"), Unique:=False
End Sub

Nếu dùng mảng mà có cả 4 điều kiện trên thì code em làm là:
Mã:
Sub LocDuLieu2()
    Dim arr(), kq(), i As Long, a As Long, lr As Long, j As Long
    lr = Sheet1.Range("A65000").End(xlUp).Row
    arr = Sheet1.Range("A4:H" & lr).Value
    ReDim kq(1 To UBound(arr), 1 To 8)
  
    For i = 1 To UBound(arr)
        If arr(i, 1) = Range("B3").Value _
        And arr(i, 2) = Range("C3").Value _
        And arr(i, 3) = Range("D3").Value _
        And arr(i, 4) = Range("E3").Value Then
            a = a + 1
            For j = 1 To 8
                kq(a, j) = arr(i, j)
            Next j
        End If
    Next i
  
Range("A7:H65000").ClearContents
On Error Resume Next ' tranh loi khi khong co ket qua
Range("A7").Resize(a, 8).Value = kq
End Sub

Còn nếu khuyết 1 trong các điều kiện thì em dùng If then else để xét lần lượt từng điều kiện cũng được nhưng như vậy là rất dài và lượm thượm.
anh chị có cách nào hay chỉ em với, Cảm ơn anh chị đã tư vấn.
Chạy code
Mã:
Sub Loc_XYZ()
  Dim sArr(), aDK(), aCol(), Res(), sRow&, sCol&, i&, k&, j&, c&, sC&
  Dim tmp$, bTest As Boolean
  With Sheet2
    aDK = .Range("B2:E3").Value 'Vung Xet Dieu Kien
  End With

  sArr = Sheet1.Range("A3:H3").Value 
  sCol = UBound(sArr, 2)
  For j = 1 To UBound(aDK, 2)
    If aDK(2, j) <> Empty Then
      For c = 1 To sCol
        If aDK(1, j) = sArr(1, c) Then
          sC = sC + 1
          aDK(1, sC) = c
          aDK(2, sC) = aDK(2, j)
          Exit For
        End If
      Next c
    End If
  Next j
  With Sheet1
    sArr = .Range("A4:H" & .Range("A65000").End(xlUp).Row).Value
  End With
  sRow = UBound(sArr)
  If sC = 0 Then
    Res = sArr: k = sRow
  Else
    ReDim Res(1 To sRow, 1 To sCol)
    For i = 1 To sRow
      bTest = True
      For c = 1 To sC
        If aDK(2, c) <> sArr(i, aDK(1, c)) Then
          bTest = False: Exit For
        End If
      Next c
      If bTest Then
        k = k + 1
        For j = 1 To sCol
          Res(k, j) = sArr(i, j)
        Next j
      End If
    Next i
  End If
  With Sheet2
    .Range("A7:H65000").ClearContents
    If k Then .Range("A7").Resize(k, sCol).Value = Res
  End With
End Sub
 
Upvote 0
Cần phải kiểm tra kết quả có đủ số lượng dòng mong muốn hay không, vì bản thân Advanced filter (chạy bằng tay) cũng có lỗi không lấy hết dữ liệu khi vượt quá 1 số dòng nào đó.
Đã thử với 388 ngàn dòng. Dữ liệu lấy tận dòng cuối, kết quả trả về đủ.
 
Upvote 0
Tôi thấy mấy bài kiểu này xài Power Bi là nhanh nhất. Chỉ cần kéo thả, chọn Slicer tuỳ biến đơn giản. Sử dụng VBA mỗi lần tuỳ biến lại phải chỉnh sửa code.
 
Upvote 0
Tôi thử dùng thì code này chưa xét trường hợp Arrcrit() có 1 hoặc nhiều phần tử có giá trị rỗng
Nhắc bạn: nick đúng của tác giả code là @siwtom chứ không phải Switom

PHP:
Sub LocDuLieu2()
    Dim arr(), kq(), i As Long, a As Long, lr As Long, j As Long
    lr = Sheet1.Range("A65000").End(xlUp).Row
    arr = Sheet1.Range("A4:H" & lr).Value
    ReDim kq(1 To UBound(arr), 1 To 8)
    For i = 1 To UBound(arr)
        If _
        IIf(Len(Range("B3")) > 0, arr(i, 1) = Range("B3").Value, True) And _
        IIf(Len([C3]) > 0, arr(i, 2) = Range("C3").Value, True) And _
        IIf(Len([D3]) > 0, arr(i, 3) = Range("D3").Value, True) And _
        IIf(Len([E3]) > 0, arr(i, 4) = Range("E3").Value, True) Then
            a = a + 1
            For j = 1 To 8
                kq(a, j) = arr(i, j)
            Next j
        End If
    Next i

Range("A7:H65000").ClearContents
On Error Resume Next ' tranh loi khi khong co ket qua'
Range("A7").Resize(a, 8).Value = kq
End Sub

PHP:
Sub LocDuLieu3()
    Dim arr(), kq(), i As Long, a As Long, lr As Long, j As Long
    Dim DkTong As Boolean, Dkien()
    lr = Sheet1.Range("A65000").End(xlUp).Row
    arr = Sheet1.Range("A4:H" & lr).Value
    Dkien = Sheet2.Range("B3:E3").Value
    ReDim kq(1 To UBound(arr), 1 To 8)
    For i = 1 To UBound(arr)
        DkTong = True
        For k = 1 To UBound(Dkien, 2)
            If Len(Dkien(1, k)) > 0 Then DkTong = DkTong And (arr(i, k) = Dkien(1, k))
            If DkTong = False Then Exit For
        Next
        If DkTong = True Then
            a = a + 1
            For j = 1 To 8
                kq(a, j) = arr(i, j)
            Next j
        End If
    Next i

Range("A7:H65000").ClearContents
On Error Resume Next ' tranh loi khi khong co ket qua
Range("A7").Resize(a, 8).Value = kq
End Sub
Điều kiện là vùng điều kiện theo đúng thứ tự cột giống dữ liệu (như file gốc của tác giả). Nếu khác thứ tự thì xử tiếp bằng 1 mảng con khác
Cảm ơn anh ! em đã hiệu chỉnh thông tin tác giả ở bài viết của mình
 
Upvote 0
Tôi thấy mấy bài kiểu này xài Power Bi là nhanh nhất. Chỉ cần kéo thả, chọn Slicer tuỳ biến đơn giản. Sử dụng VBA mỗi lần tuỳ biến lại phải chỉnh sửa code.
bạn nói Power BI hay Power query? Power BI chỉ để xem dạng báo cáo quản trị, không tạo ra báo cáo in theo mẫu được. Đa số các doanh nghiệp hiện tại vẫn còn xem báo cáo in, hoặc báo cáo gởi qua mail theo mẫu quy định riêng từng doanh nghiệp
So với code LocDulieu3() bài #12 thì bỏ qua thứ tự cột của vùng điều kiện, nhưng hơi phức tạp (nhiều vòng lặp). Như đã nói trong bài #12, tôi chỉ thêm mảng hoặc nới rộng mảng chứ không thêm vòng lặp
 
Lần chỉnh sửa cuối:
Upvote 0
bạn nói Power BI hay Power query? Power BI chỉ để xem dạng báo cáo quản trị, không tạo ra báo cáo in theo mẫu được. Đa số các doanh nghiệp hiện tại vẫn còn xem báo cáo in, hoặc báo cáo gởi qua mail theo mẫu quy định riêng từng doanh nghiệp
Em thấy Power Bi vẫn tạo được báo cáo in được anh! Chỉ là không phải bản thương mại thì chỉ xuất ra PDF được thôi, người nhận chỉ xem được nội dung chứ không chỉnh sửa được!
 
Upvote 0
Cách của chú ptm0412 rất gọn gàng, Hàm IFF thì con chưa biết, nhưng con sẽ nghiên cứu code của chú!
Cảm ơn chú nhất nhiều.
 
Upvote 0
Web KT

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

Back
Top Bottom