Function Filter2DArray(ByVal sArray, ByVal ColIndex As Long, ByVal FindStr1 As String, ByVal HasTitle As Boolean, _
Optional ByVal FindStr2, Optional ByVal arg_and As Boolean = True)
' sArray là mảng - range chứa các giá trị cần lọc
' ColIndex là chỉ số cột mà các giá trị ở đấy sẽ được lọc. Chỉ số này được tính từ 1 bất kể chỉ số sArray được tính từ bao nhiêu.
' vd. ta truyền mảng sArray thực chất là mảng sArray(4 to 35, 5 to 10) mà ta muốn lọc theo cột thứ 2 thì ColIndex = 2
' FindStr1(2) là mask để kiểm tra xem có khớp với các giá trị trong cột ColIndex hay không
' Nếu khớp thì toàn bộ dòng có chứa giá trị khớp đó sẽ được trả về
' Có 3 loại MASK
' 1. loại "><=xyz" - xyz là các chữ số
' 2. loại "!xyz" - lấy các dòng mà ở cột ColIndex không có cụm ký tự "xyz", tức loại các dòng mà ở cột ColIndex có chứa "xyz"
' 3. loại "xyz" - lấy các dòng mà ở cột ColIndex có cụm ký tự "xyz"
' HasTitle - thông báo là mảng nhập vào có chứa tiêu đề ở dòng đầu tiên (TRUE) hoặc không chứa (FALSE)
Dim TmpArr, i As Long, j As Long, Arr, Dic, TmpStr, Tmp, Chk As Boolean, TmpVal As Double, sArr As String, sFind As String, res As Boolean
Dim currRow As Long
On Error Resume Next
Set Dic = CreateObject("Scripting.Dictionary")
' sao dữ liệu từ sArray sang TmpArr
TmpArr = sArray
' ColIndex là chỉ số cột tìm kiếm trong sArray tính từ 1, cột đó trong TmpArr có chỉ số:
ColIndex = ColIndex + LBound(TmpArr, 2) - 1
' kiểm tra xem ký tự đầu tiên trong mask FindStr1 có phải là "<", ">" hay "=" không. Nếu có thì mask là loại 1, tức ta tìm và lấy
' các dòng có chứa số "abc" thỏa điều kiện abc ><=xyz.
If FindStr1 <> "" Then
Chk = InStr("><=", Left(FindStr1, 1)) > 0
If Not IsMissing(FindStr2) And (FindStr2 <> "") Then Chk = Chk And (InStr("><=", Left(FindStr1, 1)) > 0)
End If
' đi từng dòng trong cột lọc ColIndex và lọc lấy dữ liệu
For i = LBound(TmpArr, 1) - HasTitle To UBound(TmpArr, 1)
If Chk Then
' nếu FindStr1 là dạng ><= xyz thì đọc giá trị tại dòng hiện hành ở cột ColIndex
TmpVal = CDbl(TmpArr(i, ColIndex))
' kiểm tra lỗi vì dữ liệu đầu vào có thể chứa lỗi
If Err.Number = 0 Then
' và nếu giá trị đó là số và thỏa mãn (TmpVal ><= xyz) thì thêm chỉ số dòng hiện hành (được chọn) vào từ điển
res = Evaluate(TmpVal & FindStr1)
If Not IsMissing(FindStr2) And (FindStr2 <> "") Then
If arg_and Then
res = res And Evaluate(TmpVal & FindStr2)
Else
res = res Or Evaluate(TmpVal & FindStr2)
End If
End If
Else
Err.Clear
End If
Else
' neu FindStr1 khong la dang ><= B
' nếu FinStr1 không là dạng ><=xyz thì kiểm tra xem ký tự đầu của FindStr1 có phải là "!" hay không, tức xem
' FindStr1 có dạng !xyz hay không.
sArr = UCase(TmpArr(i, ColIndex))
If Left(FindStr1, 1) = "!" Then
' FindStr có dạng "!xyz" vậy ta thêm vào từ điển chỉ số dòng hiện hành nếu giá trị ở cột ColIndex không chứa cụm "xyz"
res = Not (sArr Like UCase(Mid(FindStr1, 2, Len(FindStr1))))
Else
' FindStr không có dạng "!xyz" vậy ta thêm vào từ điển chỉ số dòng hiện hành nếu giá trị ở cột ColIndex chứa cụm "xyz"
res = sArr Like UCase(FindStr1)
End If
' nếu có thông số thứ 2
If Not IsMissing(FindStr2) Then
If Left(FindStr2, 1) = "!" Then
' FindStr2 có dạng "!xyz" vậy ta thêm vào từ điển chỉ số dòng hiện hành nếu giá trị ở cột ColIndex không chứa cụm "xyz"
If arg_and Then
res = res And Not (sArr Like UCase(Mid(FindStr2, 2, Len(FindStr2))))
Else
res = res Or Not (sArr Like UCase(Mid(FindStr2, 2, Len(FindStr2))))
End If
Else
' FindStr2 không có dạng "!xyz" vậy ta thêm vào từ điển chỉ số dòng hiện hành nếu giá trị ở cột ColIndex chứa cụm "xyz"
If arg_and Then
res = res And (sArr Like UCase(FindStr2))
Else
res = res Or (sArr Like UCase(FindStr2))
End If
End If
End If
End If
If res Then Dic.Add i, ""
Next
' nếu trong từ điển có dữ liệu là các chỉ số dòng được chọn thì các chỉ số đó là các Key trong mảng Keys
If Dic.count > 0 Then
Tmp = Dic.Keys
' tạo mảng Arr có số dòng bằng số chỉ số dòng được chọn và số cột bằng số côt của mảng nguồn sArray
ReDim Arr(LBound(TmpArr, 1) To UBound(Tmp) + LBound(TmpArr, 1) - HasTitle, LBound(TmpArr, 2) To UBound(TmpArr, 2))
' ghi các dòng của mảng nguồn mà có chỉ số là các phần tử cua Dic.Keys (tức các dòng được lấy) vào mảng Arr
For i = LBound(TmpArr, 1) - HasTitle To UBound(Tmp) + LBound(TmpArr, 1) - HasTitle
currRow = i - LBound(TmpArr, 1) + HasTitle
For j = LBound(TmpArr, 2) To UBound(TmpArr, 2)
Arr(i, j) = TmpArr(Tmp(currRow), j)
Next
Next
' nếu mảng nguồn có chứa tiêu đề thì ghi tiêu đề vào mảng Arr ở dòng đầu tiên
If HasTitle Then
For j = LBound(TmpArr, 2) To UBound(TmpArr, 2)
Arr(LBound(TmpArr, 1), j) = TmpArr(LBound(TmpArr, 1), j)
Next
End If
End If
' trả về mảng các dòng được chọn - lọc
Filter2DArray = Arr
End Function