1

The code is supposed to find strings in column A of a worksheet , if cde is found, copy and paste it into the "new" worksheet, if cde not found, continue to find efg and if efg found, copy and paste into the "new" worksheet.

May i ask for advices or opinions on how i can use functions other than .find to reduce the runtime?

Dim LongRow As Long
LongRow = wks.Range("A" & wks.Rows.Count).End(xlUp).Row + 1

'Find efg if not hij if not klm
Set c = wksData.Columns("A:A").Find("efg ", LookIn:=xlValues)

If Not c Is Nothing Then
    wks.Cells(LongRow, 9).Value = c.Value
Else
    Set f = wksData.Columns("A:A").Find("hij", LookIn:=xlValues)
    If Not f Is Nothing Then
        wks.Cells(LongRow, 9).Value = f.Value
    Else
        Set g = wksData.Columns("A:A").Find("klm", LookIn:=xlValues)
        If Not g Is Nothing Then
            wks.Cells(LongRow, 9).Value = g.Value
        End If
    End If
End If         
chris neilsen
  • 52,446
  • 10
  • 84
  • 123
  • 1
    `.Find` is the fastest. One of the other alternatives available is [AutoFilter](https://stackoverflow.com/questions/11631363/how-to-copy-a-line-in-excel-using-a-specific-word-and-pasting-to-another-excel-s/11633207#11633207) – Siddharth Rout Dec 17 '19 at 04:07
  • 2
    I think Match is the fastest out of Match, Find and looping over an array - numbers here: https://fastexcel.wordpress.com/2011/10/26/match-vs-find-vs-variant-array-vba-performance-shootout/ But if you're only running 3 lookups it's not going to make a noticeable difference. – Tim Williams Dec 17 '19 at 06:13
  • 1
    ...assuming you're looking for exact match, which is not clear from your question/code. When using `Find` you should be specific and include the `LookAt` argument (xlPart/xlWhole) – Tim Williams Dec 17 '19 at 06:22
  • 1
    Judging by your comments and other people's responses, it seems as if there's confusion about your search criteria (ie is it a full or partial match? is it case sensitive? etc.) Could you edit your question to include some sample data and search strings. – Ambie Dec 18 '19 at 00:06

3 Answers3

1

There are a few ways that you could speed this up. A simple one might be to create a custom sort order for your items, sort your data in that order, and then simply take the first row of data.

Skeleton code would look a bit like this:

Const FIRST_DATA_ROW As Long = 2

Dim wksData As Worksheet, wks As Worksheet
Dim tempRng As Range, sortRng As Range
Dim searchItems As Variant, item As Variant
Dim lastCol As Long, lastRow As Long, rowCount As Long, n As Long
Dim output() As Long
Dim found As Boolean

'HOUSEKEEPING FIRST.
'-------------------
'Define your sheets as required.
Set wksData = ThisWorkbook.Worksheets("Sheet1")
Set wks = ThisWorkbook.Worksheets("Sheet2")

'Define data limits.
With wksData
    lastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
    lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With

'Create a temp column to store original sort order.
rowCount = lastRow - FIRST_DATA_ROW + 1
ReDim output(1 To rowCount, 1 To 1)
For n = 1 To rowCount
    output(n, 1) = n
Next
Set tempRng = wksData.Cells(FIRST_DATA_ROW, lastCol + 1).Resize(rowCount)
tempRng.Value = output

'SORT THE DATA.
'--------------
'Define the search items in order.
searchItems = Array( _
    "efg", _
    "hij", _
    "klm")

Application.AddCustomList searchItems

'Execute the sort.
With wksData
    Set sortRng = .Range( _
        .Cells(FIRST_DATA_ROW, "A"), _
        .Cells(lastRow, lastCol + 1))
End With

With wksData.Sort
    With .SortFields
        .Clear
        .Add _
            Key:=sortRng.Columns("A"), _
            SortOn:=xlSortOnValues, _
            Order:=xlAscending, _
            CustomOrder:=Join(searchItems, ","), _
            DataOption:=xlSortNormal
    End With
    .SetRange sortRng
    .Header = xlNo
    .MatchCase = False
    .Orientation = xlTopToBottom
    .SortMethod = xlPinYin
    .Apply
End With

'DATA TRANSFER
'-------------

'Check if first item is in the search list.
For Each item In searchItems
    If item = wksData.Cells(FIRST_DATA_ROW, "A").Value2 Then
        found = True
        Exit For
    End If
Next

If Not found Then
    'Nothing is found.
    MsgBox "No items found."
Else
    'Copy data to new sheet.
    With wks
        lastRow = .Cells(.Rows.Count, "I").End(xlUp).Row
        .Cells(lastRow + 1, "I").Value = wksData.Cells(FIRST_DATA_ROW, "A").Value2
    End With
End If

'CLEAN UP
'--------

'Unsort data.
With wksData.Sort
    With .SortFields
        .Clear
        .Add _
            Key:=tempRng, _
            SortOn:=xlSortOnValues, _
            Order:=xlAscending, _
            DataOption:=xlSortNormal
    End With
    .SetRange sortRng
    .Header = xlNo
    .MatchCase = False
    .Orientation = xlTopToBottom
    .SortMethod = xlPinYin
    .Apply
End With

'Clear the temp sort order.
tempRng.ClearContents

Another way would be to log each time you hit a match and just look for higher priority items. Once you've reached a top priority item, then end the search. Again, skeleton code could be like this:

Const FIRST_DATA_ROW As Long = 2

Dim wksData As Worksheet, wks As Worksheet
Dim searchItems As Variant, data As Variant
Dim priority As Long, foundIndex As Long, i As Long, j As Long

'Define your sheets as required.
Set wksData = ThisWorkbook.Worksheets("Sheet1")
Set wks = ThisWorkbook.Worksheets("Sheet2")

'Define the search items in order.
searchItems = Array( _
    "efg", _
    "hij", _
    "klm")

'Read the data into an array.
With wksData
    data = .Range( _
                .Cells(FIRST_DATA_ROW, "A"), _
                .Cells(.Rows.Count, "A").End(xlUp)) _
            .Value2
End With

'Loop through the data.
priority = -1
foundIndex = -1
For i = 1 To UBound(data, 1)
    'Look for a match.
    For j = 0 To UBound(searchItems)
        If data(i, 1) = searchItems(j) Then
            'We have a match, so ...
            'Store the values.
            foundIndex = i
            priority = j
            'Remove lower priority search items.
            If priority > 0 Then ReDim Preserve searchItems(j - 1)
            Exit For
        End If
    Next
    'Stop if we have a top priority match.
    If priority = 0 Then Exit For
Next

'Copy the data.
If foundIndex = -1 Then
    'Nothing is found.
    MsgBox "No items found."
Else
    'Copy data to new sheet.
    With wks
        .Cells(.Rows.Count, "I").End(xlUp).Offset(1).Value = data(foundIndex, 1)
    End With
End If
Ambie
  • 4,872
  • 2
  • 12
  • 26
  • 1
    hi, thank you so much for the answer! it runs perfectly fine without any errors, but somehow it's always `no items found` but i'm very sure the data specified can be found, may i know if this is a match case function? because i don't think i can use that since i have to search for "efg" then copy the whole string that comes after "efg" as well then copy the data into a new sheet. This may seem rude or thoughtless but are you ok with explaining with me the `loop` part of your 2nd code? –  Dec 17 '19 at 07:20
  • 1
    If you're looking for a substring within a string then replace `If data(i, 1) = ...` with `If data(i, 1) Like "*" & searchItems(j) & "*" Then`. And if you want it also to be case insensitive then add `Option Compare Text` at the very top of your module. The loop simply iterates your data array, checking each row one by one. – Ambie Dec 18 '19 at 03:49
  • 1
    Hi @Ambie i know it might a little too long from my last reply, but may i clarify one question? after copying the data to the new sheet, because some of my files will consist none of the 3 arrays specified above and when that happens the certain row will be empty, and i would like to insert `"Hang"` to indicate that it hanged instead of the row being an empty one, but it doesn't show up . –  Dec 24 '19 at 03:23
  • 1
    Here's the code ` 'Copy data to new sheet. With wks wks.Cells(BlankRow, 10).value = data(foundIndex, 1) End With If wks Is Nothing Then wks.Cells(BlankRow, 10).value = "Hang" End If End If ` –  Dec 24 '19 at 03:23
1

Here is a short code that will search the column only 1x for all your searched sub-strings and should be faster, Try ...

    Sub SearchColumn()

    Dim ws1 As Worksheet, ws2 As Worksheet

    Dim ws1Row As Integer, ws2Row As Integer
    Dim ws1Col As Integer, ws2Col As Integer
    Dim ws1RowMax As Integer

    Dim searchedText(2) As String
    Dim cellText As String

    searchedText(0) = "efg"
    searchedText(1) = "hij"
    searchedText(2) = "klm"

    Set ws1 = Worksheets(1)
    Set ws2 = Worksheets(2)

    ws1Row = 1
    ws1Col = 1 '-- col A, 2 = col B, etc.

    ws2Row = 1
    ws2Col = 1 '-- col A, 2 = col B, etc.

    ws1RowMax = 100 '-- choose a number (can be max - 65000
                    ' or program for last cell.
                    ' There is a protection in the For loop -
                    '  - if a cell is empty, routine will quit

    With ws1
        For ws1Row = 1 To ws1RowMax
            cellText = .Cells(ws1Row, ws1Col)
            If (cellText = "") Then Exit For
            check = CheckMatch(cellText, searchedText)
            If (check) Then
                ws2.Cells(ws2Row, ws2Col) = cellText
                ws2Row = ws2Row + 1
            End If
            ws1Row = ws1Row + 1
            If (ws1Row > ws1RowMax) Then Exit For
        Next ws1Row
    End With

End Sub

Function CheckMatch(textToCheck As String, searchedText() As String) As Boolean
    Dim result As Boolean
    For Each txt In searchedText
        If (InStr(textToCheck, txt)) Then
            result = True
            Exit For
        End If
    Next
    CheckMatch = result
End Function

For this to run as is, make sure you have at least 2 sheets in your workbook. In 1st sheet place your text data in column 1 - or change the number of 'ws1Col' to the number of the column where your text is. You may also need to adjust the starting row in the For loop from 1 to any number where your text search starts. Hope this helps.

Felix
  • 1,662
  • 2
  • 18
  • 37
  • 1
    hi, i really appreciate the help u given, because i am using `option explicit` somehow when running the code, it stops at `check` and says something like `check` is not declared may i know how should i declare it? –  Dec 17 '19 at 07:02
  • 1
    Yes, just place `Dim check as boolean` somewhere with the other declarations above – Felix Dec 17 '19 at 15:27
1

For exact match, Match is very fast, but for 3 items you're not going to notice the difference

Dim m, v

For Each v in Array("efg","hij","klm")
    m = Application.Match(v, wksData.Columns("A:A"), 0)
    If Not IsError(m) Then
        wks.Range("A" & wks.Rows.Count).End(xlUp).Offset(1, 0).Value = _
                    wksData.Cells(m, 1).Value 
        Exit For
    End If
Next v

If you're not looking for exact match you can use

m = Application.Match(v & "*", wksData.Columns("A:A"), 0)       'look for "begins with v"
m = Application.Match("*" & v & "*", wksData.Columns("A:A"), 0) 'look for "contains v"
Tim Williams
  • 154,628
  • 8
  • 97
  • 125
  • 1
    hi, this is exactly what i am looking for! and i do have at least 20-30 items i will look for, but what if i don't want it to be exact match? i am searching for "efg" but there will be "zxczxc" behind it meaning the original string is "efg zxczxc". I would like to search for "efg" and copy paste the whole string. is that possible with `match`? –  Dec 17 '19 at 06:56
  • 1
    `m = Application.Match(v & "*", wksData.Columns("A:A"), 0)` to use `Match` for "begins with" – Tim Williams Dec 17 '19 at 19:12
  • 1
    hi, sorry for the late reply, thanks for the code, it worked perfectly fine! may i know if i'm supposed to repeat this multiple times and i would like to have only one for loop, is that possible? i have a for loop that iterates through a specified folder by me and after iterating, i will have to perform the match function to look for many different strings. –  Dec 23 '19 at 03:15
  • 1
    It's not very clear what you need to do - are you saying you need to open all the files in a folder and looks for certain strings? The same one set of strings for each file? You can place your search strings in a list on a worksheet and read them from there. – Tim Williams Dec 23 '19 at 06:05
  • 1
    @Tim Williams There are about 10+ search strings and i will have to place them into different columns from A to K, after i have found them in the individual files e.g efg into column A and hij into column B and klm into column C. –  Dec 24 '19 at 03:25