Results 1 to 14 of 14

Thread: [RESOLVED] Autocomplete textbox

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Nov 2005
    Posts
    1,834

    Resolved [RESOLVED] Autocomplete textbox

    Hi, I'm using the code below to create some sort of autocomplete textbox.

    swords is an array with more than 100.000 words. Every time the user enters a letter into the textbox (txtWord), it loops though the swords array and uses the Like operator to find matching words and puts them into a listbox (lstWordCheck).

    This works fine when the word starts with 'a', but when the word starts with 'z', then with every letter entered into the textbox, it has to loop all the way to the end of the array and that's very slow.

    Is there a faster way to find matching words instead of having to loop always from the beginning?

    vb Code:
    1. Option Explicit
    2.  
    3. Private blnBackSpace As Boolean
    4.  
    5. Private Sub txtWord_Change()
    6.     Dim i As Long
    7.     Dim x As Long
    8.    
    9.     If Len(txtWord.Text) = 0 Then Exit Sub
    10.    
    11.     If blnBackSpace = True Then
    12.         blnBackSpace = False
    13.         Exit Sub
    14.     End If
    15.    
    16.     lstWordCheck.Clear
    17.    
    18.     For i = LBound(swords) To UBound(swords)
    19.         If swords(i) Like txtWord.Text & "*" Then
    20.             ListAddItem lstWordCheck, swords(i)
    21.             x = x + 1
    22.             If x = 100 Then Exit For
    23.         End If
    24.     Next i
    25. End Sub
    26.  
    27. Private Sub txtWord_KeyDown(KeyCode As Integer, Shift As Integer)
    28. If KeyCode = vbKeyBack Or KeyCode = vbKeyDelete Then
    29.     If Len(txtWord.Text) <> 0 Then
    30.         blnBackSpace = True
    31.     End If
    32. End If
    33. End Sub

  2. #2
    Discovering Life Siddharth Rout's Avatar
    Join Date
    Feb 2005
    Location
    Mumbai, India
    Posts
    12,001

    Re: Autocomplete textbox

    Why an array? Why not a listbox or a combobox to store 100,000 words ?

    Use sendmessage API as shown in the link below. It is pretty much faster than looping

    http://www.vbforums.com/showpost.php...54&postcount=5
    A good exercise for the Heart is to bend down and help another up...
    Please Mark your Thread "Resolved", if the query is solved


    MyGear:
    ★ CPU ★ Ryzen 5 5800X
    ★ GPU ★ NVIDIA GeForce RTX 3080 TI Founder Edition
    ★ RAM ★ G. Skill Trident Z RGB 32GB 3600MHz
    ★ MB ★ ASUS TUF GAMING X570 (WI-FI) ATX Gaming
    ★ Storage ★ SSD SB-ROCKET-1TB + SEAGATE 2TB Barracuda IHD
    ★ Cooling ★ NOCTUA NH-D15 CHROMAX BLACK 140mm + 10 of Noctua NF-F12 PWM
    ★ PSU ★ ANTEC HCG-1000-EXTREME 1000 Watt 80 Plus Gold Fully Modular PSU
    ★ Case ★ LIAN LI PC-O11 DYNAMIC XL ROG (BLACK) (G99.O11DXL-X)
    ★ Monitor ★ LG Ultragear 27" 240Hz Gaming Monitor
    ★ Keyboard ★ TVS Electronics Gold Keyboard
    ★ Mouse ★ Logitech G502 Hero

  3. #3

  4. #4
    Discovering Life Siddharth Rout's Avatar
    Join Date
    Feb 2005
    Location
    Mumbai, India
    Posts
    12,001

    Re: Autocomplete textbox

    Quote Originally Posted by RhinoBull View Post
    That's a bad idea - if number if items exceeds integer value returned index maybe be (and usually is) incorrect.
    Sorry I didn't get you?
    A good exercise for the Heart is to bend down and help another up...
    Please Mark your Thread "Resolved", if the query is solved


    MyGear:
    ★ CPU ★ Ryzen 5 5800X
    ★ GPU ★ NVIDIA GeForce RTX 3080 TI Founder Edition
    ★ RAM ★ G. Skill Trident Z RGB 32GB 3600MHz
    ★ MB ★ ASUS TUF GAMING X570 (WI-FI) ATX Gaming
    ★ Storage ★ SSD SB-ROCKET-1TB + SEAGATE 2TB Barracuda IHD
    ★ Cooling ★ NOCTUA NH-D15 CHROMAX BLACK 140mm + 10 of Noctua NF-F12 PWM
    ★ PSU ★ ANTEC HCG-1000-EXTREME 1000 Watt 80 Plus Gold Fully Modular PSU
    ★ Case ★ LIAN LI PC-O11 DYNAMIC XL ROG (BLACK) (G99.O11DXL-X)
    ★ Monitor ★ LG Ultragear 27" 240Hz Gaming Monitor
    ★ Keyboard ★ TVS Electronics Gold Keyboard
    ★ Mouse ★ Logitech G502 Hero

  5. #5

    Thread Starter
    Frenzied Member
    Join Date
    Nov 2005
    Posts
    1,834

    Re: Autocomplete textbox

    Thank you, koolsid.

    The reason I have a sorted array, is because I need to do other things with the words as well, like fast binary searches.

    I just tried your code and it shows the results instantly, but loading more than 100.000 words into an array barely takes 3 seconds (with Split). Loading the words into a listbox or combobox takes minutes, even if I hide the combobox or use LockWindowUpdate. I just tried to load 300.000 words into a combobox and it took more than 5 minutes.

  6. #6
    Discovering Life Siddharth Rout's Avatar
    Join Date
    Feb 2005
    Location
    Mumbai, India
    Posts
    12,001

    Re: Autocomplete textbox

    I just tried to load 300.000 words into a combobox and it took over 5 minutes.
    How and from where are you loading it?
    A good exercise for the Heart is to bend down and help another up...
    Please Mark your Thread "Resolved", if the query is solved


    MyGear:
    ★ CPU ★ Ryzen 5 5800X
    ★ GPU ★ NVIDIA GeForce RTX 3080 TI Founder Edition
    ★ RAM ★ G. Skill Trident Z RGB 32GB 3600MHz
    ★ MB ★ ASUS TUF GAMING X570 (WI-FI) ATX Gaming
    ★ Storage ★ SSD SB-ROCKET-1TB + SEAGATE 2TB Barracuda IHD
    ★ Cooling ★ NOCTUA NH-D15 CHROMAX BLACK 140mm + 10 of Noctua NF-F12 PWM
    ★ PSU ★ ANTEC HCG-1000-EXTREME 1000 Watt 80 Plus Gold Fully Modular PSU
    ★ Case ★ LIAN LI PC-O11 DYNAMIC XL ROG (BLACK) (G99.O11DXL-X)
    ★ Monitor ★ LG Ultragear 27" 240Hz Gaming Monitor
    ★ Keyboard ★ TVS Electronics Gold Keyboard
    ★ Mouse ★ Logitech G502 Hero

  7. #7
    PowerPoster RhinoBull's Avatar
    Join Date
    Mar 2004
    Location
    New Amsterdam
    Posts
    24,132

    Re: Autocomplete textbox

    Quote Originally Posted by koolsid View Post
    Sorry I didn't get you?
    Run this first:
    Code:
    Private Sub Command1_Click()
    Dim i As Long
    
        For i = 0 To 32768
            List1.AddItem i
        Next i
        
        Debug.Print List1.List(List1.ListCount - 1)
        Debug.Print List1.List(List1.ListCount)
    
    End Sub
    and then do this:
    Code:
    Private Sub List1_Click()
        Debug.Print List1.List(List1.ListIndex) 'try last item and at least one before
    End Sub

  8. #8
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,974

    Re: Autocomplete textbox

    Quote Originally Posted by Chris001
    The reason I have a sorted array, is because I need to do other things with the words as well, like fast binary searches.
    As you are just checking the start of the word, I would use a Binary search for this too.

    The result you get from that will be the first match (or the item before it), so you only need to loop from there onwards until you find an item that doesn't match.

  9. #9

    Thread Starter
    Frenzied Member
    Join Date
    Nov 2005
    Posts
    1,834

    Re: Autocomplete textbox

    Quote Originally Posted by koolsid View Post
    How and from where are you loading it?
    I loaded the words by looping through the swords array.

    vb Code:
    1. For i = LBound(swords) To UBound(swords)
    2.     Combo1.AddItem swords(i)
    3. Next i

  10. #10

    Thread Starter
    Frenzied Member
    Join Date
    Nov 2005
    Posts
    1,834

    Re: Autocomplete textbox

    Quote Originally Posted by si_the_geek View Post
    As you are just checking the start of the word, I would use a Binary search for this too.

    The result you get from that will be the first match (or the item before it), so you only need to loop from there onwards until you find an item that doesn't match.
    That seems to work fine and is very fast.

    vb Code:
    1. Private Sub txtWord_Change()
    2.     Dim i As Long
    3.     Dim x As Long
    4.     Dim lngStart As Long
    5.    
    6.     If Len(txtWord.Text) = 0 Then Exit Sub
    7.    
    8.     If blnBackSpace = True Then
    9.         blnBackSpace = False
    10.         Exit Sub
    11.     End If
    12.    
    13.     lstWordCheck.Clear
    14.    
    15.     lngStart = BinarySearchLike(swords(), txtWord.Text)
    16.    
    17.     If lngStart > 0 Then lngStart = lngStart - 1
    18.     If lngStart = -1 Then Exit Sub
    19.    
    20.     For i = lngStart To UBound(swords)
    21.         If swords(i) Like txtWord.Text & "*" Then
    22.             ListAddItem lstWordCheck, swords(i)
    23.             x = x + 1
    24.             If x = 100 Then Exit For
    25.         End If
    26.     Next i
    27. End Sub

    My original BinarySearch function only searched for full words and I changed this.

    Code:
    Public Function BinarySearchLike(strArray() As String, strSearch As String) As Long
        Dim lngFirst As Long
        Dim lngLast As Long
        Dim lngMiddle As Long
        Dim bolInverseOrder As Boolean
        lngFirst = LBound(strArray)
        lngLast = UBound(strArray)
        bolInverseOrder = (strArray(lngFirst) > strArray(lngLast))
        BinarySearchLike = lngFirst - 1
        Do
            lngMiddle = (lngFirst + lngLast) \ 2
            'If strArray(lngMiddle) = strSearch Then
            If strArray(lngMiddle) Like strSearch & "*" Then
                BinarySearchLike = lngMiddle
                Exit Do
            ElseIf ((strArray(lngMiddle) < strSearch) Xor bolInverseOrder) Then
                lngFirst = lngMiddle + 1
            Else
                lngLast = lngMiddle - 1
            End If
        Loop Until lngFirst > lngLast
    End Function

  11. #11
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,974

    Re: Autocomplete textbox

    I would actually recommend changing the binary search back, because with Like you cannot be sure if it has found the first match or not - it may even be the last one.

    If you use the = it will always find the first (or the item before it if it isn't an exact match).



    You can also make the loop after it a bit faster, by re-arranging like this:
    Code:
        For i = lngStart To UBound(swords)
            If NOT swords(i) Like txtWord.Text & "*" Then Exit For
            ListAddItem lstWordCheck, swords(i)
            x = x + 1
            If x = 100 Then Exit For
        Next i
    ..this way it will exit as soon as a non-matching item is found (as you are checking the left, you shouldn't be able to find any more matches).

  12. #12

    Thread Starter
    Frenzied Member
    Join Date
    Nov 2005
    Posts
    1,834

    Re: Autocomplete textbox

    But the problem is that it doesn't return anything until a 100&#37; match has been found.

    t = returns nothing (not in the array)
    th = returns nothing (not in the array)
    the = returns a list of matches starting with the (is in the array)
    thec = returns nothing (not in the array)
    theca = returns a list of matches starting with theca (is in the array)
    thecas = returns nothing (not in the array)

    When the user for example types 'thec' into the textbox, I'd like to return everything that starts with 'thec', eventhough the word itself doesn't exist in the array.


    Thanks for the improvement.

  13. #13
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,974

    Re: Autocomplete textbox

    Ah yes, that is because you only set the functions return value on an exact match.

    What I would recommend (so that you can use the function for other purposes too) is add an optional parameter which allows a 'closest' match too, eg:
    Code:
    Public Function BinarySearchLike(strArray() As String, strSearch As String, Optional p_booReturnClosestIfNoMatch as Boolean = False) As Long
    ...
            If strArray(lngMiddle) = strSearch Then
                BinarySearchLike = lngMiddle
                Exit Function
            ElseIf ((strArray(lngMiddle) < strSearch) Xor bolInverseOrder) Then
                lngFirst = lngMiddle + 1
            Else
                lngLast = lngMiddle - 1
            End If
        Loop Until lngFirst > lngLast
    
        If p_booReturnClosestIfNoMatch Then
          BinarySearchLike = lngLast  'or maybe lngFirst?
        End If
    End Function

  14. #14

    Thread Starter
    Frenzied Member
    Join Date
    Nov 2005
    Posts
    1,834

    Re: Autocomplete textbox

    Yes, that works great with lngFirst.

    Thank you.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width