[TẶNG] Thuật toán lọc tìm kiếm mới trên ComboBox.

Liên hệ QC

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,590
Được thích
16,653
Giới tính
Nam
Mặc dù cách lọc mới này cũng là những vòng lặp để duyệt từng hàng trên mảng như những thuật toán lọc hiện hành, nhưng tôi "phát minh" ra một kiểu lọc mới có thể giảm được thời gian từ bằng việc lọc thông thường cho đến nhanh hơn rất nhiều.

Cụ thể là lọc thông thường các coder thường lấy mảng gốc để lọc cho bất cứ từ khóa (key) nào, còn tôi sẽ lọc được mảng nào lưu tạm lại mảng đó để dùng lọc cho các từ khóa sau. Vì vậy khi bạn gõ ký tự đầu tiên sẽ có thời gian lọc như các kiểu lọc thông thường, nhưng từ ký tự thứ 2 và thứ n sẽ giảm dần theo dữ liệu còn lại trên mảng lưu tạm.

Và cứ mỗi ký tự bị xóa nó sẽ trả lại mảng tương ứng đã lọc trước đó cho nên nó không mất thời gian cho việc lọc lại.

Code này tôi viết để lọc cho ComboBox, nhưng nếu ai muốn tùy biến trên TextBox và gán dữ liệu vào ListBox cũng không vấn đề gì.

Và code này tôi xin tặng các bạn nhân mùa dịch khủng khiếp này. Tôi cũng rất mong được các bạn góp ý cho những trường hợp làm cho nó hoàn thiện hơn, nhanh hơn.

Tôi tạm lấy hơn 11 ngàn phường xã trong nước x 10 lần để có số hàng 111,620 dòng để test.

PHP:
Private Sub cbxPhuongXa_Change()
    If cbxPhuongXa.Text = "" Then
        ReDim Preserve priArrPhuongXa(0 To 0)
        cbxPhuongXa.Column = priArrPhuongXa(0)
        GoTo ExitSub
    End If
 
    On Error GoTo ExitSub
    Dim c As Long, lngLenText As Long, lngUbd As Long
 
    lngLenText = Len(cbxPhuongXa.Text)
    lngUbd = UBound(priArrPhuongXa)
     
    c = lngLenText - 1
 
    If Not IsArray(priArrPhuongXa(c)) Then
        cbxPhuongXa.Clear
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
        GoTo ExitSub
    End If
 
    If lngUbd > lngLenText Then
        If Not IsArray(priArrPhuongXa(lngLenText)) Then
            cbxPhuongXa.Clear
            cbxPhuongXa.ForeColor = &H800000
        Else
            cbxPhuongXa.Column = priArrPhuongXa(lngLenText)
        End If
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
        GoTo ExitSub
    End If
     
    If Right(cbxPhuongXa.Text, 1) = "*" Or Right(cbxPhuongXa.Text, 1) = "?" Then
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
        priArrPhuongXa(lngLenText) = priArrPhuongXa(lngUbd)
        GoTo ExitSub
    End If
     
    Dim arrFilter()
    Dim strType As String, strTemp As String, strColOne As String
    Dim n As Long, r As Long, t As Long, uCol As Long, uRow As Long
     
    c = lngLenText - 1
    lRow = LBound(priArrPhuongXa(c), 2): uRow = UBound(priArrPhuongXa(c), 2)
 
    strTemp = UCase(LoaiDauUni(cbxPhuongXa.Text))
    strType = "*" & strTemp & "*"
    strTypeTwo = strTemp & "*"
     
    For r = lRow To uRow
        strColOne = UCase(LoaiDauUni(priArrPhuongXa(c)(0, r)))
        If strColOne Like strType Then
            ReDim Preserve arrFilter(0 To 0, 0 To n)
            arrFilter(0, n) = priArrPhuongXa(c)(0, r)
            n = n + 1
        End If
    Next
 
    If n Then
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
        priArrPhuongXa(lngLenText) = arrFilter
        cbxPhuongXa.Column = arrFilter
    Else
        cbxPhuongXa.Clear
        cbxPhuongXa.ForeColor = &H800000
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
    End If

ExitSub:
    If cbxPhuongXa.ListCount > 0 Then cbxPhuongXa.DropDown
End Sub

Tôi cũng khuyến mại thêm cho các bạn Hàm LoaiDauUni để loại bỏ dấu tiếng Việt kiểu gõ Unicode (dựng sẵn).

PHP:
Function LoaiDauUni(ByVal strText As String) As String
    If strText = "" Then Exit Function
    Static ObjDict As Object
    Static blnInitial As Boolean
 
    If Not blnInitial Then
        Dim c As Byte
        Dim arrNoMarks, arrUnicode
        arrUnicode = Array(192, 193, 194, 195, 200, 201, 202, 204, 205, 210, 211, 212, _
                            213, 217, 218, 221, 224, 225, 226, 227, 232, 233, 234, 236, 237, _
                            242, 243, 244, 245, 249, 250, 253, 258, 259, 272, 273, 296, 297, _
                            360, 361, 416, 417, 431, 432, 7840, 7841, 7842, 7843, 7844, 7845, _
                            7846, 7847, 7848, 7849, 7850, 7851, 7852, 7853, 7854, 7855, 7856, _
                            7857, 7858, 7859, 7860, 7861, 7862, 7863, 7864, 7865, 7866, 7867, _
                            7868, 7869, 7870, 7871, 7872, 7873, 7874, 7875, 7876, 7877, 7878, _
                            7879, 7880, 7881, 7882, 7883, 7884, 7885, 7886, 7887, 7888, 7889, _
                            7890, 7891, 7892, 7893, 7894, 7895, 7896, 7897, 7898, 7899, 7900, _
                            7901, 7902, 7903, 7904, 7905, 7906, 7907, 7908, 7909, 7910, 7911, _
                            7912, 7913, 7914, 7915, 7916, 7917, 7918, 7919, 7920, 7921, 7922, _
                            7923, 7924, 7925, 7926, 7927, 7928, 7929)
        arrNoMarks = Array("A", "A", "A", "A", "E", "E", "E", "I", "I", "O", "O", "O", "O", _
                            "U", "U", "Y", "a", "a", "a", "a", "e", "e", "e", "i", "i", "o", "o", _
                            "o", "o", "u", "u", "y", "A", "a", "D", "d", "I", "i", "U", "u", "O", _
                            "o", "U", "u", "A", "a", "A", "a", "A", "a", "A", "a", "A", "a", "A", _
                            "a", "A", "a", "A", "a", "A", "a", "A", "a", "A", "a", "A", "a", "E", _
                            "e", "E", "e", "E", "e", "E", "e", "E", "e", "E", "e", "E", "e", "E", _
                            "e", "I", "i", "I", "i", "O", "o", "O", "o", "O", "o", "O", "o", "O", _
                            "o", "O", "o", "O", "o", "O", "o", "O", "o", "O", "o", "O", "o", "O", _
                            "o", "U", "u", "U", "u", "U", "u", "U", "u", "U", "u", "U", "u", "U", _
                            "u", "Y", "y", "Y", "y", "Y", "y", "Y", "y")
        Set ObjDict = CreateObject("Scripting.Dictionary")
        For c = 0 To 133
            ObjDict(arrUnicode(c)) = arrNoMarks(c)
        Next
        blnInitial = True
    End If
 
    Dim i As Long, j As Long, lngAscW As Long
    For i = 1 To Len(strText)
        lngAscW = AscW(Mid(strText, i, 1))
        If lngAscW > 191 Then
            Mid(strText, i, 1) = ObjDict.Item(lngAscW)
        End If
    Next
    LoaiDauUni = strText
End Function

P/S: Nếu muốn lọc mà không cần hàm LoaiDauUni để nhanh hơn, hãy xem bài #58.
 

File đính kèm

  • FilterTest-HTN.xlsm
    1.1 MB · Đọc: 246
Lần chỉnh sửa cuối:
Mặc dù cách lọc mới này cũng là những vòng lặp để duyệt từng hàng trên mảng như những thuật toán lọc hiện hành, nhưng tôi phát minh ra một kiểu lọc mới có thể giảm được thời gian từ bằng việc lọc thông thường cho đến nhanh hơn rất nhiều.

Cụ thể là lọc thông thường các coder thường lấy mảng gốc để lọc cho bất cứ từ khóa (key) nào, còn tôi sẽ lọc được mảng nào lưu tạm lại mảng đó để dùng lọc cho các từ khóa sau. Vì vậy khi bạn gõ ký tự đầu tiên sẽ có thời gian lọc như các kiểu lọc thông thường, nhưng từ ký tự thứ 2 và thứ n sẽ giảm dần theo dữ liệu còn lại trên mảng lưu tạm.

Và cứ mỗi ký tự bị xóa nó sẽ trả lại mảng tương ứng đã lọc trước đó cho nên nó không mất thời gian cho việc lọc lại.

Code này tôi viết để lọc cho ComboBox, nhưng nếu ai muốn tùy biến trên TextBox và gán dữ liệu vào ListBox cũng không vấn đề gì.

Và code này tôi xin tặng các bạn nhân mùa dịch khủng khiếp này. Tôi cũng rất mong được các bạn góp ý cho những trường hợp làm cho nó hoàn thiện hơn, nhanh hơn.

Tôi tạm lấy hơn 11 ngàn phường xã trong nước x 10 lần để có số hàng 111,620 dòng để test.

PHP:
Private Sub cbxPhuongXa_Change()
    If cbxPhuongXa.Text = "" Then
        ReDim Preserve priArrPhuongXa(0 To 0)
        cbxPhuongXa.Column = priArrPhuongXa(0)
        GoTo ExitSub
    End If
  
    On Error GoTo ExitSub
    Dim c As Long, lngLenText As Long, lngUbd As Long
  
    lngLenText = Len(cbxPhuongXa.Text)
    lngUbd = UBound(priArrPhuongXa)
      
    c = lngLenText - 1
  
    If Not IsArray(priArrPhuongXa(c)) Then
        cbxPhuongXa.Clear
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
        GoTo ExitSub
    End If
  
    If lngUbd > lngLenText Then
        If Not IsArray(priArrPhuongXa(lngLenText)) Then
            cbxPhuongXa.Clear
            cbxPhuongXa.ForeColor = &H800000
        Else
            cbxPhuongXa.Column = priArrPhuongXa(lngLenText)
        End If
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
        GoTo ExitSub
    End If
      
    If Right(cbxPhuongXa.Text, 1) = "*" Or Right(cbxPhuongXa.Text, 1) = "?" Then
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
        priArrPhuongXa(lngLenText) = priArrPhuongXa(lngUbd)
        GoTo ExitSub
    End If
      
    Dim arrFilter()
    Dim strType As String, strTemp As String, strColOne As String
    Dim n As Long, r As Long, t As Long, uCol As Long, uRow As Long
      
    c = lngLenText - 1
    lRow = LBound(priArrPhuongXa(c), 2): uRow = UBound(priArrPhuongXa(c), 2)
  
    strTemp = UCase(LoaiDauUni(cbxPhuongXa.Text))
    strType = "*" & strTemp & "*"
    strTypeTwo = strTemp & "*"
      
    For r = lRow To uRow
        strColOne = UCase(LoaiDauUni(priArrPhuongXa(c)(0, r)))
        If strColOne Like strType Then
            ReDim Preserve arrFilter(0 To 0, 0 To n)
            arrFilter(0, n) = priArrPhuongXa(c)(0, r)
            n = n + 1
        End If
    Next
  
    If n Then
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
        priArrPhuongXa(lngLenText) = arrFilter
        cbxPhuongXa.Column = arrFilter
    Else
        cbxPhuongXa.Clear
        cbxPhuongXa.ForeColor = &H800000
        ReDim Preserve priArrPhuongXa(0 To lngLenText)
    End If

ExitSub:
    If cbxPhuongXa.ListCount > 0 Then cbxPhuongXa.DropDown
End Sub

Tôi cũng khuyến mại thêm cho các bạn Hàm LoaiDauUni để loại bỏ dấu tiếng Việt kiểu gõ Unicode (dựng sẵn).

PHP:
Function LoaiDauUni(ByVal strText As String) As String
    If strText = "" Then Exit Function
    Static ObjDict As Object
    Static blnInitial As Boolean
  
    If Not blnInitial Then
        Dim c As Byte
        Dim arrNoMarks, arrUnicode
        arrUnicode = Array(192, 193, 194, 195, 200, 201, 202, 204, 205, 210, 211, 212, _
                            213, 217, 218, 221, 224, 225, 226, 227, 232, 233, 234, 236, 237, _
                            242, 243, 244, 245, 249, 250, 253, 258, 259, 272, 273, 296, 297, _
                            360, 361, 416, 417, 431, 432, 7840, 7841, 7842, 7843, 7844, 7845, _
                            7846, 7847, 7848, 7849, 7850, 7851, 7852, 7853, 7854, 7855, 7856, _
                            7857, 7858, 7859, 7860, 7861, 7862, 7863, 7864, 7865, 7866, 7867, _
                            7868, 7869, 7870, 7871, 7872, 7873, 7874, 7875, 7876, 7877, 7878, _
                            7879, 7880, 7881, 7882, 7883, 7884, 7885, 7886, 7887, 7888, 7889, _
                            7890, 7891, 7892, 7893, 7894, 7895, 7896, 7897, 7898, 7899, 7900, _
                            7901, 7902, 7903, 7904, 7905, 7906, 7907, 7908, 7909, 7910, 7911, _
                            7912, 7913, 7914, 7915, 7916, 7917, 7918, 7919, 7920, 7921, 7922, _
                            7923, 7924, 7925, 7926, 7927, 7928, 7929)
        arrNoMarks = Array("A", "A", "A", "A", "E", "E", "E", "I", "I", "O", "O", "O", "O", _
                            "U", "U", "Y", "a", "a", "a", "a", "e", "e", "e", "i", "i", "o", "o", _
                            "o", "o", "u", "u", "y", "A", "a", "D", "d", "I", "i", "U", "u", "O", _
                            "o", "U", "u", "A", "a", "A", "a", "A", "a", "A", "a", "A", "a", "A", _
                            "a", "A", "a", "A", "a", "A", "a", "A", "a", "A", "a", "A", "a", "E", _
                            "e", "E", "e", "E", "e", "E", "e", "E", "e", "E", "e", "E", "e", "E", _
                            "e", "I", "i", "I", "i", "O", "o", "O", "o", "O", "o", "O", "o", "O", _
                            "o", "O", "o", "O", "o", "O", "o", "O", "o", "O", "o", "O", "o", "O", _
                            "o", "U", "u", "U", "u", "U", "u", "U", "u", "U", "u", "U", "u", "U", _
                            "u", "Y", "y", "Y", "y", "Y", "y", "Y", "y")
        Set ObjDict = CreateObject("Scripting.Dictionary")
        For c = 0 To 133
            ObjDict(arrUnicode(c)) = arrNoMarks(c)
        Next
        blnInitial = True
    End If
  
    Dim i As Long, j As Long, lngAscW As Long
    For i = 1 To Len(strText)
        lngAscW = AscW(Mid(strText, i, 1))
        If lngAscW > 191 Then
            Mid(strText, i, 1) = ObjDict.Item(lngAscW)
        End If
    Next
    LoaiDauUni = strText
End Function
Kỹ thuật lọc sáng tạo và rất hay, xem lại vài vấn đề code sẽ hoàn hảo hơn
Kết quả lọc còn trùng, ví dụ nhập "xa phu cha" hoặc "xa phu chau"
Tốc độ code còn chậm do hàm LoaiDauUni gọi quá nhiều lần xử lý cho cùng 1 dữ liệu, và ObjDict được tạo nhiều lần không cần thiết
Khi số ký tự nhập nhiều, mãng lưu trữ kết quả priArrPhuongXa phình to tốn bộ nhớ
Form thích hợp cho nhập từ bàn phím, nếu chọn 1 dòng dữ liệu từ danh sách sau đó xóa bớt vài ký tự code không lọc được
 
Upvote 0
Kỹ thuật lọc sáng tạo và rất hay, xem lại vài vấn đề code sẽ hoàn hảo hơn
Kết quả lọc còn trùng, ví dụ nhập "xa phu cha" hoặc "xa phu chau"
Tốc độ code còn chậm do hàm LoaiDauUni gọi quá nhiều lần xử lý cho cùng 1 dữ liệu, và ObjDict được tạo nhiều lần không cần thiết
Khi số ký tự nhập nhiều, mãng lưu trữ kết quả priArrPhuongXa phình to tốn bộ nhớ
Form thích hợp cho nhập từ bàn phím, nếu chọn 1 dòng dữ liệu từ danh sách sau đó xóa bớt vài ký tự code không lọc được
Nói về hàm LoaiDauUni trước đi.
Tôi đã tối ưu cho nó rất kỹ. Cái Dic nó chỉ được tạo ra đúng một lần đầu tiên chạy hàm, còn lại nó đã được lưu vào bộ nhớ tạm, cần tra "từ điển" là nó ra, không cần phải tạo lại. Bạn nên tham khảo kiểu biến được khai báo bằng Static.
 
Upvote 0
Nói về hàm LoaiDauUni trước đi.
Tôi đã tối ưu cho nó rất kỹ. Cái Dic nó chỉ được tạo ra đúng một lần đầu tiên chạy hàm, còn lại nó đã được lưu vào bộ nhớ tạm, cần tra "từ điển" là nó ra, không cần phải tạo lại. Bạn nên tham khảo kiểu biến được khai báo bằng Static.
Mình không nhầm về dic :)
Có cách nào 1 dữ liệu chỉ cần chạy LoaiDauUni 1 lần không
 
Upvote 0
Mình không nhầm về dic :)
Có cách nào 1 dữ liệu chỉ cần chạy LoaiDauUni 1 lần không
Cơ bản là nó cũng làm cho code chạy chậm tí xíu, nhưng nó đã nhanh hơn những hàm loại dấu Uni trước đây rồi đó bởi vì nó chỉ tốn vòng lặp đầu tiên để lưu trữ key, còn những lần sau nó chỉ xuất item chứ không làm gì nữa.

Bạn có thể thử dùng MsgBox và đặt trong If Not blnInitial Then để kiểm tra sẽ thấy MsgBox chạy đúng 1 lần duy nhất.
 
Upvote 0
Cơ bản là nó cũng làm cho code chạy chậm tí xíu, nhưng nó đã nhanh hơn những hàm loại dấu Uni trước đây rồi đó bởi vì nó chỉ tốn vòng lặp đầu tiên để lưu trữ key, còn những lần sau nó chỉ xuất item chứ không làm gì nữa.

Bạn có thể thử dùng MsgBox và đặt trong If Not blnInitial Then để kiểm tra sẽ thấy MsgBox chạy đúng 1 lần duy nhất.
Mã:
    For r = lRow To uRow
        strColOne = UCase(LoaiDauUni(priArrPhuongXa(c)(0, r)))
        If strColOne Like strType Then
            ReDim Preserve arrFilter(0 To 0, 0 To n)
            arrFilter(0, n) = priArrPhuongXa(c)(0, r)
            n = n + 1
        End If
    Next
strColOne = UCase(LoaiDauUni(priArrPhuongXa(c)(0, r)))
Mỗi lần nhập thêm ký tự lệnh trên sẽ chạy lại dữ liệu đã xử lý
 
Upvote 0
Nhưng tôi thắc mắc rằng khi gõ từ khóa tìm kiếm chẳng may gõ nhầm xóa đi gõ lại thì kết quả sẽ khác vì mảng gốc đã loại bỏ mất rồi
 
Upvote 0
Code này không dùng được trong trường hợp tổng quát. Vì muốn dùng thì phải nhập dữ liệu theo đúng trình tự mà bạn bắt buộc. Vd. tôi nhập "sương" và có loạt kết quả chứa "sương". Lúc này tôi mới nhận ra lỗi chính tả, phải là "xương". Tôi muốn click trước hoặc sau "s" rồi dùng Delete hoặc Backspace để xóa "s" thì không được. Trỏ văn bản cứ nhẩy về cuối khi tôi click ở giữa từ, nên tôi phải nhấn phím mũi tên trái 4 lần. Sau khi xóa "s" tôi nhấn "x" để có "xương" thì không có kết quả nào dù dữ liệu có "xương". Hoặc lẽ ra phải gõ "Phong" thì do nhấn phím hơi lâu nên thành "Phoong". Bây giờ có xóa 1 "o" thì cũng không có kết quả dù dữ liệu có "Phong". Nhớ danh sách cuối để lọc từ đó thì nhiều người cũng nghĩ tới rồi chứ không phải là điều mới, nhưng nó đòi hỏi nhập và chỉnh sửa theo một cách nhất định. Ngoài ra nếu tôi gõ "phu" là tôi muốn tìm "phu" chứ không phải cả "phù", "phủ", "phũ", "phú", "phụ".

Tôi viết nhanh một code mà bạn gọi là thuật toán lọc hiện hành, lấy mảng gốc để lọc. Bạn thử kiểm tra tốc độ xem. Cứ gõ ký tự 1, rồi 2, ...

Đố bạn biết chỗ nhanh ở đâu, cái gì nó làm code nhanh hẳn. :D
 

File đính kèm

  • ListBox tim kiem nhanh.xlsm
    1.1 MB · Đọc: 136
Lần chỉnh sửa cuối:
Upvote 0
Tác giả bài đăng thân mến, Mình thấy hay & áp dụng thử thấy OK,
Bạn cần xem xét thêm trường hợp:
Mình muốn tìm 'Xã Phú Tài'; (Thực tế không có tên xã này)
Mình gõ đến chuỗi 'Xã Phú T" thì ra danh sách cỡ 5 đến 60 địa danh có từ cuối là Thị, túc, thành, tiến, . . . . .
Đến đây mình gõ thêm ký tự 'a' nó đưa ra các địa danh có đuôi là Tân & Tâm (không ít)
Mình bỏ dấu huyền trên chữ 'a' thì danh sách trong ComboBox không suy xuyễn;
Mình muốn trường hợp này hiện trong ComboBox là 'Nothing', được không?

Chú thích: Ngã 3 Phú Tài ở Bình Định chắc là thị trấn hay gì gì khác chăng?
 
Lần chỉnh sửa cuối:
Upvote 0
Tác giả bài đăng thân mến, Mình thấy hay & áp dụng thử thấy OK,
Bạn cần xem xét thêm trường hợp:
Mình muốn tìm 'Xã Phú Tài'; (Thực tế không có tên xã này)
Mình gõ đến chuỗi 'Xã Phú T" thì ra danh sách cỡ 5 đến 60 địa danh có từ cuối là Thị, túc, thành, tiến, . . . . .
Đến đây mình gõ thêm ký tự 'a' nó đưa ra các địa danh có đuôi là Tân & Tâm (không ít)
Mình bỏ dấu huyền trên chữ 'a' thì danh sách trong ComboBox không suy xuyễn;
Mình muốn trường hợp này hiện trong ComboBox là 'Nothing', được không?

Chú thích: Ngã 3 Phú Tài ở Bình Định chắc là thị trấn hay gì gì khác chăng?
Thủ phạm là LoaiDauUni. Vì thế bạn gõ "a" mà nó tìm cả "a" có thêm dấu như bài #8 tôi đã viết ("a" có thêm dấu đi qua cái máy chém LoaiDauUni nó loại hết dấu chỉ còn "a" nên khớp với "a" đã gõ)
 
Upvote 0
Để tôi mô tả sơ về thuật toán này cho anh em rõ.

1) Vì sao tôi dùng hàm LoaiDauUni? Để khi tìm kiếm không cần gọi dấu nó cũng gợi ý cho tất cả các trường hợp liên quan đến nguyên âm hoặc phụ âm (Đ, đ) có chứa dấu hoặc không. Chính vì điều này nó sẽ liệt kê những thứ không cần thiết (nhưng cũng một phần do dữ liệu trùng đến 10 lần nên những dữ liệu na ná nó sẽ nhân lên 10 lần vì thế nó hơi rối mắt). Tuy nhiên, hãy nhìn Google, mình gõ không dấu/có dấu nó cũng liệt kê tất cả các trường hợp có dấu hoặc không dấu.

2) Vì sao nó nhanh hơn? Bởi vì nó lấy dữ liệu liền kề để lọc cho ký tự sau nên bỏ qua các công đoạn lọc trước.

3) Khi nó lưu mảng tạm trước đó, nếu ta gõ sai ký tự vừa gõ ta back lại ký tự đó thì nó trả lại mảng ta vừa lọc mà không phải lọc lại lần nữa. Và nếu gõ thêm ký tự mới nó vẫn sẽ lấy mảng liền kề để lọc nên vẫn sẽ rất nhanh.

4) Cũng chính vì điều này nên nó có một hạn chế là việc cho sửa chen ngang, nó chỉ cho ta sửa xóa chỉ 1 ký tự được xếp cuối cùng vì thế tôi mới dùng sự kiện MouseUp để ngăn đặt con trỏ vào các vị trí trước ký tự cuối. Nói chung cái này tôi nghĩ cũng không làm khó người gõ cho lắm (hoặc do tôi gõ mười ngón không nhìn bàn phím mà chỉ nhìn màn hình nên tôi phát hiện ngay lỗi mình gõ nên chủ quan chăng?!).

5) Và có thể còn nhiều khuyết điểm khác, nhưng qua bài này tôi tin rằng sẽ có nhiều cải tiến cho những code lỗi thời nhằm khắc phục giới hạn tốc độ.

Cám ơn mọi người đã chỉ ra những khiếm khuyết (đã lường trước và chưa lường trước) trong bài này.
 
Upvote 0
Để tôi mô tả sơ về thuật toán này cho anh em rõ.

1) Vì sao tôi dùng hàm LoaiDauUni? Để khi tìm kiếm không cần gọi dấu nó cũng gợi ý cho tất cả các trường hợp liên quan đến nguyên âm hoặc phụ âm (Đ, đ) có chứa dấu hoặc không. Chính vì điều này nó sẽ liệt kê những thứ không cần thiết (nhưng cũng một phần do dữ liệu trùng đến 10 lần nên những dữ liệu na ná nó sẽ nhân lên 10 lần vì thế nó hơi rối mắt). Tuy nhiên, hãy nhìn Google, mình gõ không dấu/có dấu nó cũng liệt kê tất cả các trường hợp có dấu hoặc không dấu.

2) Vì sao nó nhanh hơn? Bởi vì nó lấy dữ liệu liền kề để lọc cho ký tự sau nên bỏ qua các công đoạn lọc trước.

3) Khi nó lưu mảng tạm trước đó, nếu ta gõ sai ký tự vừa gõ ta back lại ký tự đó thì nó trả lại mảng ta vừa lọc mà không phải lọc lại lần nữa. Và nếu gõ thêm ký tự mới nó vẫn sẽ lấy mảng liền kề để lọc nên vẫn sẽ rất nhanh.

4) Cũng chính vì điều này nên nó có một hạn chế là việc cho sửa chen ngang, nó chỉ cho ta sửa xóa chỉ 1 ký tự được xếp cuối cùng vì thế tôi mới dùng sự kiện MouseUp để ngăn đặt con trỏ vào các vị trí trước ký tự cuối. Nói chung cái này tôi nghĩ cũng không làm khó người gõ cho lắm (hoặc do tôi gõ mười ngón không nhìn bàn phím mà chỉ nhìn màn hình nên tôi phát hiện ngay lỗi mình gõ nên chủ quan chăng?!).

5) Và có thể còn nhiều khuyết điểm khác, nhưng qua bài này tôi tin rằng sẽ có nhiều cải tiến cho những code lỗi thời nhằm khắc phục giới hạn tốc độ.

Cám ơn mọi người đã chỉ ra những khiếm khuyết (đã lường trước và chưa lường trước) trong bài này.
Tập tin của tôi và của bạn cùng có dữ liệu như nhau. Bạn đã thử gõ, vd. "p", rồi "h" xem tốc độ trong 2 tập tin chưa?
 
Upvote 0
Đố bạn biết chỗ nhanh ở đâu, cái gì nó làm code nhanh hẳn. :D
Để em trả lời bài này:
1) Nó không dùng hàm loại dấu,
2) Nó không cần xét chữ hoa thường (Option Compare Text), nên không dùng hàm UCase,
3) Ad một mảng trắng sẽ nhanh hơn phương thức Clear.

Và em cũng thắc mắc tại sao thêm 2 dòng rỗng ở dưới cho dữ liệu để rồi lại trừ đi 2?
 
Lần chỉnh sửa cuối:
Upvote 0
Thế bạn thử bỏ + 2 và xóa hết dữ liệu chỉ để tiêu đề trong A1 xem sao nhé. Mà + 2 nhưng sau đó không xét 2 dòng cuối thì "hòa cả làng" mà. Hoặc chỉ + 1 rồi chạy xem sao nhé.

Loại dấu và UCase cũng chưa chắc là thủ phạm chính.

Bạn hãy chạy code của tôi. Sau đó thay
Mã:
ReDim result(1 To 1, 1 To 1)
    ListBox1.List = result
    ListBox1.RemoveItem 0

bằng

Mã:
ListBox1.Clear

rồi lại chạy và so sánh nhé. Cả 2 đều có FOR với 111620 vòng mà code 1 nhanh hơn nhiều. Vậy FOR cũng chả là cái đinh gì. Thủ phạm nhìn rất ngây thơ, vô tội ...
 
Upvote 0
Thế bạn thử bỏ + 2 và xóa hết dữ liệu chỉ để tiêu đề trong A1 xem sao nhé. Mà + 2 nhưng sau đó không xét 2 dòng cuối thì "hòa cả làng" mà. Hoặc chỉ + 1 rồi chạy xem sao nhé.

Loại dấu và UCase cũng chưa chắc là thủ phạm chính.

Bạn hãy chạy code của tôi. Sau đó thay
Mã:
ReDim result(1 To 1, 1 To 1)
    ListBox1.List = result
    ListBox1.RemoveItem 0

bằng

Mã:
ListBox1.Clear

rồi lại chạy và so sánh nhé. Cả 2 đều có FOR với 111620 vòng mà code 1 nhanh hơn nhiều. Vậy FOR cũng chả là cái đinh gì. Thủ phạm nhìn rất ngây thơ, vô tội ...
Ở bài #13 em có thêm vô mục 3 đúng với cái vụ Clear này.

Vấn đề này bẫy lỗi dữ liệu "rỗng" cho chuẩn thì em cũng sẽ làm như sau:

1) Sẽ không add dữ liệu nếu e < 2

2) ActiveSheet.AutoFilterMode = False
 
Upvote 0
Ở bài #13 em có thêm vô mục 3 đúng với cái vụ Clear này.

Vấn đề này bẫy lỗi dữ liệu "rỗng" cho chuẩn thì em cũng sẽ làm như sau:

1) Sẽ không add dữ liệu nếu e < 2

2) ActiveSheet.AutoFilterMode = False
Trong rất nhiều sub nếu không có dữ liệu thì tôi dùng Exit Sub. Nhưng chuyện +2, +1 thì dĩ nhiên phải có. Vì nếu vẫn code ấy mà tôi không cộng gì trong khi A2 có dữ liệu, tức không RỖNG, thì vẫn sẽ có lỗi. Vậy trong trường hợp này phải +1. Mà đã +1 thì làm luôn +2 để phục vụ nốt trường hợp cả A2 cũng rỗng.
 
Upvote 0
Trong rất nhiều sub nếu không có dữ liệu thì tôi dùng Exit Sub. Nhưng chuyện +2, +1 thì dĩ nhiên phải có. Vì nếu vẫn code ấy mà tôi không cộng gì trong khi A2 có dữ liệu, tức không RỖNG, thì vẫn sẽ có lỗi. Vậy trong trường hợp này phải +1. Mà đã +1 thì làm luôn +2 để phục vụ nốt trường hợp cả A2 cũng rỗng.
Nhưng xét cho cùng, nếu em áp dụng code của em bỏ qua hàm Loại dấu thì từ ký tự thứ 2 trở đi vẫn nhanh hơn vì số vòng lặp được rút ngắn đáng kể so với dữ liệu ban đầu.
 
Upvote 0
Code này không dùng được trong trường hợp tổng quát. Vì muốn dùng thì phải nhập dữ liệu theo đúng trình tự mà bạn bắt buộc. Vd. tôi nhập "sương" và có loạt kết quả chứa "sương". Lúc này tôi mới nhận ra lỗi chính tả, phải là "xương". Tôi muốn click trước hoặc sau "s" rồi dùng Delete hoặc Backspace để xóa "s" thì không được. Trỏ văn bản cứ nhẩy về cuối khi tôi click ở giữa từ, nên tôi phải nhấn phím mũi tên trái 4 lần. Sau khi xóa "s" tôi nhấn "x" để có "xương" thì không có kết quả nào dù dữ liệu có "xương". Hoặc lẽ ra phải gõ "Phong" thì do nhấn phím hơi lâu nên thành "Phoong". Bây giờ có xóa 1 "o" thì cũng không có kết quả dù dữ liệu có "Phong". Nhớ danh sách cuối để lọc từ đó thì nhiều người cũng nghĩ tới rồi chứ không phải là điều mới, nhưng nó đòi hỏi nhập và chỉnh sửa theo một cách nhất định. Ngoài ra nếu tôi gõ "phu" là tôi muốn tìm "phu" chứ không phải cả "phù", "phủ", "phũ", "phú", "phụ".

Tôi viết nhanh một code mà bạn gọi là thuật toán lọc hiện hành, lấy mảng gốc để lọc. Bạn thử kiểm tra tốc độ xem. Cứ gõ ký tự 1, rồi 2, ...

Đố bạn biết chỗ nhanh ở đâu, cái gì nó làm code nhanh hẳn. :D
File này của anh, e nhập chữ Xa xong cách ra nó ra kết quả như hình, rõ ràng là còn rất nhiều xã nữa?

Snag_37ba73e.png
cảm ơn anh.
 
Upvote 0
File này của anh, e nhập chữ Xa xong cách ra nó ra kết quả như hình, rõ ràng là còn rất nhiều xã nữa?

View attachment 265622
cảm ơn anh.
Dĩ nhiên là nó sẽ liệt kê tất cả những chữ liên quan đến xa có dấu và không dấu. Bởi vì nó được Loại dấu nên tất cả đều thành "xa".
Cách search khôn ngoan là chọn các từ khóa không phổ biến (kể cả search google).
 
Upvote 0
Dĩ nhiên là nó sẽ liệt kê tất cả những chữ liên quan đến xa có dấu và không dấu. Bởi vì nó được Loại dấu nên tất cả đều thành "xa".
Cách search khôn ngoan là chọn các từ khóa không phổ biến (kể cả search google).
Tôi chưa thử vì chưa có nhu cầu lọc nhưng theo hình chụp bài 17 thì dù gõ xã hay xa hay bất kỳ cái gì thì list hiện ra cần phải lọc duy nhất. Google đâu có trùng như vậy.
 
Upvote 0
Web KT
Back
Top Bottom