1

I have a Excel workbook with two sheets, A and B.

Sheet A contains a lot of different unstructured data, sheet B, Col A, contains up to 100 strings (Each has 1-4 words), one per row.

I am using VBA to loop thru all rows in sheet A, find cells i a Col E in sheet A that contains the strings in Sheet B. If found, the row where it is found is copied to a Sheet C.

So far, all is good, the code I am using is working fine.

My problem is that the data in sheet B is static, and I wish to make it dynamic so strings can be added or deleted. The way I will do this is to populate sheet B from a database where the strings are being kept updated.

The code I am using will only test for the first word in sheet B, it will not move on to the next if the first is not found.

The code need to loop thru Sheet A, and for each row it need to loop thru the list of words in Sheet B, until it find the word, do the copy paste, and then move on to the next row in Sheet A.

The list of words in Sheet B will be much shorter if I could use RegEx when looking in Sheet A.

So my question is, I think that to do this I will need to save each string in sheet B as a variable, and then use this variable in the RegEx expression, and use that RegEx in the find?

Or does anyone has a different idea, and maybe point me in the right direction on how to write the missing code?

The code I am using now is:

Sub Find()
 Dim sh1 As Worksheet, sh2 As Worksheet, rng As Range, cel As Range
 Dim rngCopy As Range, lastR1 As Long, lastR2 As Long
 Dim strSearch1 As String, strSearch2 As String
 Dim va, vb
 Dim i As Long
 Dim d As Object
 

 Set sh1 = ActiveSheet
 Set sh2 = Worksheets("SheetC")
 
 lastR1 = sh1.Range("E" & Rows.Count).End(xlUp).Row
 lastR2 = sh2.Range("A" & Rows.Count).End(xlUp).Row + 1
 
 With Sheets("SheetB")  'this is where the list is
    va = .Range("A1", .Cells(.Rows.Count, "A").End(xlUp))
 End With
 
        Set d = CreateObject("scripting.dictionary")
        d.CompareMode = vbTextCompare
        For i = 1 To UBound(va, 1)
            d(va(i, 1)) = Empty
        Next
 
        vb = sh1.Range("E1:E" & lastR1)
        For i = 2 To UBound(vb, 1)
           If d.Exists(vb(i, 1)) Then
               If rngCopy Is Nothing Then
                   Set rngCopy = sh1.Rows(i)
               Else
                   Set rngCopy = Union(rngCopy, sh1.Rows(i))
               End If
           End If
        Next
 
    If Not rngCopy Is Nothing Then
       rngCopy.Copy Destination:=sh2.Cells(lastR2, 1)
    End If
End Sub
Peter
  • 11
  • 1
  • Check the methods here: https://stackoverflow.com/q/22542834/4961700 – Solar Mike Dec 27 '22 at 10:46
  • What do you mean *will only test for the first word in sheet B*. `d(va(i, 1)) = Empty` is storing the string ie. all the words. – CDP1802 Dec 27 '22 at 11:17
  • What I mean is that when running the code it will only look for the string in A1 in sheet B. If it is found it will copy paste, but if it is not found, it will just move to the next row in Sheet A without testing the strings in A2, A3, A4 and so on to see if any of these strings are found – Peter Dec 27 '22 at 11:20
  • `d.Exists(vb(i, 1))` will test for match against all the keys in the dictionary, ie all those in `va`. It's a bit confusing having va = SheetB and vb = SheetA :) – CDP1802 Dec 27 '22 at 11:24
  • OK, now I am a little outside my "VBA comfortzone". Where in my code should I add in d.Exists(vb(i, 1)) ? – Peter Dec 27 '22 at 11:28
  • Brainstop for me for a moment, thinking one thing and typing a different thing. What I meant is: it does not do that. For some reason it is not testing for all strings. I have checked that there are no spaces or anything else I can think of can create a issue, but it still happening. – Peter Dec 27 '22 at 11:49
  • Ok I think I understand. The dictionary will only find exact matches but I guess you want "sheet A that **contains** the strings in Sheet B". So match the sentence `this word here` with the string `word here`. Correct ? – CDP1802 Dec 27 '22 at 11:55
  • Correct, so the strings in sheet B could also be a substring in the cell in sheet A. (bad explanation from my side) – Peter Dec 27 '22 at 12:56

1 Answers1

0
Option Explicit

Sub FindString()

    Dim wsA As Worksheet, wsB As Worksheet, wsC As Worksheet
    Dim lastA As Long, lastB As Long, lastC As Long
    Dim va, vb, rngCopy As Range, n As Long, i As Long
   
    With ThisWorkbook
        Set wsA = .Sheets("SheetA")
        Set wsB = .Sheets("SheetB")
        Set wsC = .Sheets("SheetC")
    End With
     
    lastA = wsA.Range("E" & Rows.Count).End(xlUp).Row
    lastB = wsB.Range("A" & Rows.Count).End(xlUp).Row
    lastC = wsC.Range("A" & Rows.Count).End(xlUp).Row + 1
    
    Dim d As Object
    Set d = CreateObject("Scripting.Dictionary")
    d.CompareMode = vbTextCompare
    
    'this is where the list is
    vb = wsB.Range("A1:A" & lastB).Value2
    For i = 1 To lastB
        d(vb(i, 1)) = Empty
    Next
    
    ' build regex pattern from dictionary keys
    Dim Regex As Object, sPattern As String
    Set Regex = CreateObject("vbscript.regexp")
    sPattern = Join(d.keys, "|")
    'Debug.Print sPattern
    
    With Regex
        .Global = False
        .MultiLine = False
        .IgnoreCase = True
        .Pattern = sPattern
    End With
    
    ' search sheetA
    va = wsA.Range("E1:E" & lastA)
    For i = 2 To lastA
       If Regex.test(va(i, 1)) Then
           If rngCopy Is Nothing Then
               Set rngCopy = wsA.Rows(i)
           Else
               Set rngCopy = Union(rngCopy, wsA.Rows(i))
           End If
           n = n + 1 ' count matches
       End If
    Next

    ' result
    If rngCopy Is Nothing Then
        MsgBox "No rows found", vbExclamation
    Else
        rngCopy.Copy Destination:=wsC.Cells(lastC, 1)
        MsgBox n & " rows found", vbInformation
    End If
    
End Sub
CDP1802
  • 13,871
  • 2
  • 7
  • 17
  • That works absolutely perfect. Thank your very much for your time, and for sharing your knowledge. – Peter Dec 27 '22 at 14:04