Search for multiple strings across multiple workbooks and copy data - excel

I have borrowed some code that searches multiple excel workbooks for a string and tried to modify it to search for multiple strings instead. Unfortunately it seems to stop after searching for the first item in the array.
Sub SearchFolders()
'Dim myArray As Variant
'Dim myCounter As Long
Dim xFso As Object
Dim xFld As Object
Dim xStrSearch As Variant
Dim xStrPath As String
Dim xStrFile As String
Dim xOut As Worksheet
Dim xWb As Workbook
Dim xWk As Worksheet
Dim xRow As Long
Dim xFound As Range
Dim xStrAddress As Variant
Dim xFileDialog As FileDialog
Dim xUpdate As Boolean
Dim xCount As Long
myArray = Array("item a", "item b", "item c", "item d")
On Error GoTo ErrHandler
Set xFileDialog = Application.FileDialog(msoFileDialogFolderPicker)
xFileDialog.AllowMultiSelect = False
xFileDialog.Title = "Select a Folder"
If xFileDialog.Show = -1 Then
xStrPath = xFileDialog.SelectedItems(1)
End If
If xStrPath = "" Then Exit Sub
xUpdate = Application.ScreenUpdating
Application.ScreenUpdating = False
Set xOut = Worksheets.Add
For myCounter = 0 To UBound(myArray)
MsgBox myCounter & "is the count no."
xStrSearch = myArray(myCounter)
MsgBox xStrSearch & "is the string"
xRow = 1
With xOut
.Cells(xRow, 1) = "Workbook"
.Cells(xRow, 2) = "Worksheet"
.Cells(xRow, 3) = "Cell"
.Cells(xRow, 4) = "Filler"
.Cells(xRow, 5) = "Text in Cell"
Set xFso = CreateObject("Scripting.FileSystemObject")
Set xFld = xFso.GetFolder(xStrPath)
xStrFile = Dir(xStrPath & "\*.xls*")
Do While xStrFile <> ""
Set xWb = Workbooks.Open(Filename:=xStrPath & "\" & xStrFile, _
UpdateLinks:=0, ReadOnly:=True, AddToMRU:=False)
For Each xWk In xWb.Worksheets
Set xFound = xWk.UsedRange.Find(xStrSearch)
If Not xFound Is Nothing Then
xStrAddress = xFound.Address
End If
Do
If xFound Is Nothing Then
Exit Do
Else
xCount = xCount + 1
xRow = xRow + 1
.Cells(xRow, 1) = xWb.Name
.Cells(xRow, 2) = xWk.Name
.Cells(xRow, 3) = xFound.Address
.Cells(xRow, 5) = xFound.Value
End If
Set xFound = xWk.Cells.FindNext(After:=xFound)
Loop While xStrAddress <> xFound.Address
myCounter = myCounter + 1
Next
xWb.Close (False)
xStrFile = Dir
Loop
.Columns("A:D").EntireColum.AutoFit
End With
Next myCounter
MsgBox xCount & "Cells have been found", , "filler"
ExitHandler:
Set xOut = Nothing
Set xWk = Nothing
Set xWb = Nothing
Set xFld = Nothing
Set xFso = Nothing
Application.ScreenUpdating = xUpdate
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
Resume ExitHandler
End Sub
Not sure what I've done wrong.
If possible, it would be great to also be able to return the value of Column A or copy Columns A - H into the sheet.
I.e. If found.address is (C,4) then also return (A,4).
Many thanks!

Whenever you have a situation where your code does multiple things like:
ask for a folder
find all files in that folder
find all cell matches in each sheet in each file
you're better off pushing any functionality which can be wrapped in its own sub/function into a stand-alone method, so you can focus on your actual task and its logic, instead of having it overwhelmed by all of the sub-tasks and their logic/rules.
Plus, once you've created your focused sub-methods, you can re-use them later.
Applying that approach to your task:
Sub SearchFolders()
Dim arrSearch As Variant
Dim rw As Long, s
Dim folderPath As String, f, allFiles As Collection, allCells As Collection
Dim wb As Workbook, ws As Worksheet, wsOut As Worksheet, c
arrSearch = Array("item a", "item b", "item c", "item d")
folderPath = SelectFolder() 'get a folder from the user
If Len(folderPath) = 0 Then Exit Sub 'no selected folder
Set allFiles = MatchingFiles(folderPath, "*.xls*") 'find all matching files
If allFiles.Count = 0 Then
MsgBox "No matching files found!"
Exit Sub
End If
Set wsOut = ThisWorkbook.Worksheets.Add()
wsOut.Range("A1:E1").Value = Array("Workbook", "Worksheet", _
"Cell", "Filler", "Cell Text")
rw = 2
For Each f In allFiles 'loop files
With Workbooks.Open(f, ReadOnly:=True)
For Each ws In .Worksheets 'loop workbooks
For Each s In arrSearch 'loop search terms
Set allCells = FindAll(ws.UsedRange, s)
For Each c In allCells 'loop matches
wsOut.Cells(rw, 1).Resize(1, 5).Value = _
Array(.Name, ws.Name, c.Address, "", c.Value)
wsOut.Cells(rw, 6).Resize(1, 8).Value = _
c.entirerow.cells(1).resize(1, 8) 'copy A-H from row with c
rw = rw + 1
Next c
Next s 'next search string
Next ws 'next worksheet
.Close False
End With
Next f 'next workbook
wsOut.Range("A:D").EntireColumn.AutoFit
End Sub
'ask the user for a folder
Function SelectFolder() As String
Dim rv As String
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
.Title = "Select a Folder"
If .Show = -1 Then rv = .SelectedItems(1)
End With
If Right(rv, 1) <> "\" Then rv = rv & "\" 'ensure trailing "\"
SelectFolder = rv
End Function
'Return a collection of all files in folder `fPath` matching `pattern`
Function MatchingFiles(fPath As String, pattern As String)
Dim f As Object, col As New Collection
With CreateObject("scripting.filesystemobject").getfolder(fPath)
For Each f In .Files
If f.Name Like pattern Then col.Add f.Path
Next f
End With
Set MatchingFiles = col
End Function
'Find all matches in a range and return as a collection of cells
'Note: adjust the Find() parameters to function as you need
' (eg. exact vs partial match)
Public Function FindAll(rng As Range, val) As Collection
Dim rv As New Collection, f As Range
Dim addr As String
Set f = rng.Find(what:=val, after:=rng.Cells(rng.Cells.Count), _
LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False)
If Not f Is Nothing Then addr = f.Address()
Do Until f Is Nothing
rv.Add f
Set f = rng.FindNext(after:=f)
If f.Address() = addr Then Exit Do
Loop
Set FindAll = rv
End Function

Related

VBA Excel - search multipe terms in workbooks with values matching to them

I have the problem as follows:
I have several workbooks in my directory, in which I want to find them by typing the code. The problem is, that apart from this code I would like to have also the value corresponding to this code. As you see in the image provided, I have the code on the cell A2 and I would like to have the value of this code (cell F2).
So far I used the code from here:
https://www.extendoffice.com/documents/excel/3354-excel-search-multiple-sheets-workbooks.html
which has been tweaked slightly and now looks like this:
Sub SearchFolders()
Dim xFso As Object
Dim xFld As Object
Dim xStrSearch As String
Dim xStrPath As String
Dim xStrFile As String
Dim xOut As Worksheet
Dim xWb As Workbook
Dim xWk As Worksheet
Dim xRow As Long
Dim xFound As Range
Dim rng As Range
Dim xStrAddress As String
Dim xFileDialog As FileDialog
Dim xUpdate As Boolean
Dim xCount As Long
Dim xAWB As Workbook
Dim xAWBStrPath As String
Dim xBol As Boolean
Dim rngValue As Variant
Set xAWB = ActiveWorkbook
xAWBStrPath = xAWB.Path & "\" & xAWB.Name
On Error GoTo ErrHandler
Set xFileDialog = Application.FileDialog(msoFileDialogFolderPicker)
xFileDialog.AllowMultiSelect = False
xFileDialog.Title = "Select a folder"
If xFileDialog.Show = -1 Then
xStrPath = xFileDialog.SelectedItems(1)
End If
If xStrPath = "" Then Exit Sub
xStrSearch = InputBox("Please provide the Code")
xUpdate = Application.ScreenUpdating
Application.ScreenUpdating = False
Set xOut = Worksheets.Add
xRow = 1
With xOut
.Cells(xRow, 1) = "Workbook"
.Cells(xRow, 2) = "Worksheet"
.Cells(xRow, 3) = "Cell"
.Cells(xRow, 4) = "Text in Cell"
.Cells(xRow, 5) = "Values corresponding"
Set xFso = CreateObject("Scripting.FileSystemObject")
Set xFld = xFso.GetFolder(xStrPath)
xStrFile = Dir(xStrPath & "\*.xls*")
Do While xStrFile <> ""
xBol = False
If (xStrPath & "\" & xStrFile) = xAWBStrPath Then
xBol = True
Set xWb = xAWB
Else
Set xWb = Workbooks.Open(Filename:=xStrPath & "\" & xStrFile, UpdateLinks:=0, ReadOnly:=True, AddToMRU:=False)
End If
For Each xWk In xWb.Worksheets
If xBol And (xWk.Name = .Name) Then
Else
Set xFound = xWk.UsedRange.Find(xStrSearch)
Set rng = xFound.EntireRow.Cells("F")
If Not xFound Is Nothing Then
xStrAddress = xFound.Address
End If
Do
If xFound Is Nothing Then
Exit Do
Else
xCount = xCount + 1
xRow = xRow + 1
.Cells(xRow, 1) = xWb.Name
.Cells(xRow, 2) = xWk.Name
.Cells(xRow, 3) = xFound.Address
.Cells(xRow, 4) = xFound.Value
.Cells(xRow, 5) = xWk.Range("F")
End If
Set xFound = xWk.Cells.FindNext(After:=xFound)
rngValue = xFound.EntireRow.Value
Loop While xStrAddress <> xFound.Address
End If
Next
If Not xBol Then
xWb.Close (False)
End If
xStrFile = Dir
Loop
.Columns("A:E").EntireColumn.AutoFit
End With
MsgBox xCount & " cells have been found", , "Code calculator"
ExitHandler:
Set xOut = Nothing
Set xWk = Nothing
Set xWb = Nothing
Set xFld = Nothing
Set xFso = Nothing
Application.ScreenUpdating = xUpdate
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
Resume ExitHandler
End Sub
I used these 2 hints:
Search for a value and return the entire row in Excel
How to search excel workbooks in folders, and subfolders for a specific string of text in Excel VBA
but I am getting following errors:
Object variable or with variable not set
for the following line:
Set rng = xFound.EntireRow.Cells("F")
whereas earlier the rng has been defined in Dim rng as Range
My second error is:
Method 'Range of object' Worksheet failed.
at the line:
.Cells(xRow, 5) = xWk.Range("F")
Is there any way of searching for the value across all workbooks in the directory and another value, which matches this value?
Based on the answer here:
Hyperlink specific cell under column on VBA code
Instead of:
.Cells(xRow, 5) = xWk.Range("F")
We need to put:
.Cells(xRow, 5).Range("A1").Value = xFound.EntireRow.Range("F1").Value
Where the Range("A1") in the given worksheet corresponds to the Range("F1") in the same workbook.
By placing the Range("A1") in the .Cells(xRow, 5) we are receiving the final value of Range("F1") in this cell.

quickly search value for multiple workbooks with specified sheets

Following the question:
VBA Excel - search multipe terms in workbooks with values matching to them
I would like to make a search for multiple workbooks across the directory, but with specified, repeatable sheets.
The full code:
Sub SearchFolders()
Dim xFso As Object
Dim xFld As Object
Dim xStrSearch As String
Dim xStrPath As String
Dim xStrFile As String
Dim xOut As Worksheet
Dim xWb As Workbook
Dim xWk As Worksheet
Dim xRow As Long
Dim xCol As Long
Dim i As Long
Dim xFound As Range
Dim xStrAddress As String
Dim xFileDialog As FileDialog
Dim xUpdate As Boolean
Dim xCount As Long
Dim xAWB As Workbook
Dim xAWBStrPath As String
Dim xBol As Boolean
Set xAWB = ActiveWorkbook
'Set xWk = ActiveWorkbook.Worksheets("Civils*")
xAWBStrPath = xAWB.Path & "\" & xAWB.Name
On Error GoTo ErrHandler
Set xFileDialog = Application.FileDialog(msoFileDialogFolderPicker)
xFileDialog.AllowMultiSelect = False
xFileDialog.Title = "Select a folder"
If xFileDialog.Show = -1 Then
xStrPath = xFileDialog.SelectedItems(1)
End If
If xStrPath = "" Then Exit Sub
'xStrSearch = "1366P"
xStrSearch = InputBox("Please provide the BoM Code")
xUpdate = Application.ScreenUpdating
Application.ScreenUpdating = False
Set xOut = Worksheets("SUMMARY")
xRow = 1
With xOut
.Cells(xRow, 1) = "Workbook"
.Cells(xRow, 2) = "Worksheet"
.Cells(xRow, 3) = "Cell"
.Cells(xRow, 4) = "Text in Cell"
.Cells(xRow, 5) = "Values corresponding"
Set xFso = CreateObject("Scripting.FileSystemObject")
Set xFld = xFso.GetFolder(xStrPath)
xStrFile = Dir(xStrPath & "\*.xls*")
Do While xStrFile <> ""
xBol = False
If (xStrPath & "\" & xStrFile) = xAWBStrPath Then
xBol = True
Set xWb = xAWB
Else
Set xWb = Workbooks.Open(Filename:=xStrPath & "\" & xStrFile, UpdateLinks:=0, ReadOnly:=True, AddToMRU:=False)
'Set xWk = Worksheets.Open("Civils Job Order")
End If
'For Each xWk In xWb.Worksheets("Civils Work Order")
For Each xWk In xWb.Worksheets
If xBol And (xWk.Name = .Name) Then
'If xBol And (xWk.Name = "Civils Work Order" Or xWk.Name = "Cable Works Order") Then
Else
Set xFound = xWk.UsedRange.Find(xStrSearch)
If Not xFound Is Nothing Then
xStrAddress = xFound.Address
End If
Do
If xFound Is Nothing Then
Exit Do
Else
xCount = xCount + 1
xRow = xRow + 1
.Cells(xRow, 1) = xWb.Name
.Cells(xRow, 2) = xWk.Name
.Cells(xRow, 3) = xFound.Address
.Cells(xRow, 4) = xFound.Value
.Cells(xRow, 5).Range("A1").Value = xFound.EntireRow.Range("F1").Value
End If
Set xFound = xWk.Cells.FindNext(After:=xFound)
Loop While xStrAddress <> xFound.Address
End If
Next
If Not xBol Then
xWb.Close (False)
End If
xStrFile = Dir
Loop
.Columns("A:E").EntireColumn.AutoFit
End With
MsgBox xCount & " cells have been found", , "BoM Calculator for VM Greenfield"
ExitHandler:
Set xOut = Nothing
Set xWk = Nothing
Set xWb = Nothing
Set xFld = Nothing
Set xFso = Nothing
Application.ScreenUpdating = xUpdate
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
Resume ExitHandler
End Sub
There is no error coming in, but the Excel is frozen indefinitely.
Is it some solution for making this kind of search for the specified worksheet names, which occur regularly across all workbooks in the directory?
This is a bit lengthy, but a lot of the bulk is re-useable functions, so it lets you focus on the logic in the main method.
I'm guessing that the summary sheet is in the same workbook as this code, and that you're scanning a folder for files to summarize, one of which may already be open in Excel (so you don't want to open that again).
Compiles but not tested...
Sub SearchFolders()
Dim wbAct As Workbook, pathMainWb As String, fldrPath As String
Dim bom As String, scrUpdt, wsOut As Worksheet, colFiles As Collection, f As Object
Dim xBol As Boolean, wb As Workbook, ws As Worksheet, arrWs
Dim matchedCells As Collection, cell, numHits As Long, summRow As Long
Set wbAct = ActiveWorkbook
pathMainWb = wbAct.FullName '<<<<
On Error GoTo ErrHandler
fldrPath = UserSelectFolder("Select a folder")
If Len(fldrPath) = 0 Then Exit Sub
'get all files in the selected folder
Set colFiles = GetFileMatches(fldrPath, "*.xls*", False) 'False=no subfolders
If colFiles.Count = 0 Then
MsgBox "No Excel files found in selected folder"
Exit Sub
End If
bom = InputBox("Please provide the BoM Code")
scrUpdt = Application.ScreenUpdating
Application.ScreenUpdating = False
Set wsOut = ThisWorkbook.Worksheets("SUMMARY")
summRow = 1
'sheet names to scan
arrWs = Array("Civils Job Order", "Civils Work Order", "Cable Works Order")
wsOut.Cells(summRow, 1).Resize(1, 5).Value = Array("Workbook", "Worksheet", _
"Cell", "Text in Cell", "Values corresponding")
For Each f In colFiles
xBol = (f.Path = pathMainWb) 'file already open?
If xBol Then
Set wb = wbAct
Else
Set wb = Workbooks.Open(Filename:=f.Path, UpdateLinks:=0, _
ReadOnly:=True, AddToMRU:=False)
End If
For Each ws In wb.Worksheets
'are we interested in this sheet?
If Not IsError(Application.Match(ws.Name, arrWs, 0)) Then
Set matchedCells = FindAll(ws.UsedRange, bom) 'get all cells with bom
If matchedCells.Count > 0 Then
For Each cell In matchedCells
summRow = summRow + 1
wsOut.Cells(summRow, 1).Resize(1, 5).Value = _
Array(wb.Name, ws.Name, cell.Address, cell.Value, _
cell.EntireRow.Range("F1").Value)
numHits = numHits + 1
Next cell 'next match
End If 'any bom matches
End If 'matched sheet name
Next ws
If Not xBol Then wb.Close False 'need to close this workbook?
Next f
wsOut.Columns("A:E").EntireColumn.AutoFit
MsgBox numHits & " cells have been found", , "BoM Calculator for VM Greenfield"
ExitHandler:
Application.ScreenUpdating = scrUpdt
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
Resume ExitHandler
End Sub
'ask the user to select a folder
Function UserSelectFolder(msgPrompt As String) As String
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
.Title = msgPrompt
If .Show = -1 Then UserSelectFolder = .SelectedItems(1) & "\"
End With
End Function
'Return a collection of file objects given a starting folder and a file pattern
' e.g. "*.txt"
'Pass False for last parameter if don't want to check subfolders
Function GetFileMatches(startFolder As String, filePattern As String, _
Optional subFolders As Boolean = True) As Collection
Dim fso, fldr, f, subFldr, fpath
Dim colFiles As New Collection
Dim colSub As New Collection
Set fso = CreateObject("scripting.filesystemobject")
colSub.Add startFolder
Do While colSub.Count > 0
Set fldr = fso.getfolder(colSub(1))
colSub.Remove 1
If subFolders Then
For Each subFldr In fldr.subFolders
colSub.Add subFldr.Path
Next subFldr
End If
fpath = fldr.Path
If Right(fpath, 1) <> "\" Then fpath = fpath & "\"
f = Dir(fpath & filePattern) 'Dir is faster...
Do While Len(f) > 0
colFiles.Add fso.getfile(fpath & f)
f = Dir()
Loop
Loop
Set GetFileMatches = colFiles
End Function
'search range `rng` for all matches to `val` and return
' as a Collection of ranges (cells)
Public Function FindAll(rng As Range, val As String) As Collection
Dim rv As New Collection, f As Range
Dim addr As String
Set f = rng.Find(what:=val, after:=rng.Cells(rng.Cells.Count), _
LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False)
If Not f Is Nothing Then addr = f.Address()
Do Until f Is Nothing
rv.Add f
Set f = rng.FindNext(after:=f)
If f.Address() = addr Then Exit Do
Loop
Set FindAll = rv
End Function

Searching Multiple Excel file using VBA with multiple search variable and output to one sheet/CSV

I am trying to modify this code, to search multiple values, from a folder containing multiple excel files, and output it to a sheet or a CSV.
The code is able to search through multiple excel sheet and output the value but the problem is that it only outputs the first search value "search_a".
The code searches a folder for the value and puts it in a new sheet.
It gives the search results for search_a but not for the others search_b, search_c..... all the search result should be in one sheet.
I know i am making some simple mistake but i am unable to identify it.
I also tried to import the search values from a .txt file list but that did not work.
Sub SearchFolders()
Dim xFso As Object
Dim xFld As Object
Dim xStrSearch As Variant
Dim xStrPath As String
Dim xStrFile As String
Dim xOut As Worksheet
Dim xWb As Workbook
Dim xWk As Worksheet
Dim xRow As Long
Dim xFound As Range
Dim xStrAddress As String
Dim xFileDialog As FileDialog
Dim xUpdate As Boolean
Dim xCount As Long
Dim xStrS As Variant
On Error GoTo ErrHandler
Set xFileDialog = Application.FileDialog(msoFileDialogFolderPicker)
xFileDialog.AllowMultiSelect = False
xFileDialog.Title = "Select a forlder"
If xFileDialog.Show = -1 Then
xStrPath = xFileDialog.SelectedItems(1)
End If
If xStrPath = "" Then Exit Sub
xUpdate = Application.ScreenUpdating
Application.ScreenUpdating = False
Set xOut = Worksheets.Add
xRow = 1
With xOut
.Cells(xRow, 1) = "Workbook"
.Cells(xRow, 2) = "Worksheet"
.Cells(xRow, 3) = "Cell"
.Cells(xRow, 4) = "Text in Cell"
Set xFso = CreateObject("Scripting.FileSystemObject")
Set xFld = xFso.GetFolder(xStrPath)
xStrFile = Dir(xStrPath & "\*.xls*")
xStrSearch = Array("search_a", "search_b", "search_c", "search_d", "search_e", "search_f")
For Each xStrS In xStrSearch
Do While xStrFile <> ""
Set xWb = Workbooks.Open(Filename:=xStrPath & "\" & xStrFile, UpdateLinks:=0, ReadOnly:=True, AddToMRU:=False)
For Each xWk In xWb.Worksheets
Set xFound = xWk.UsedRange.Find(xStrSearch)
If Not xFound Is Nothing Then
xStrAddress = xFound.Address
End If
Do
If xFound Is Nothing Then
Exit Do
Else
xCount = xCount + 1
xRow = xRow + 1
.Cells(xRow, 1) = xWb.Name
.Cells(xRow, 2) = xWk.Name
.Cells(xRow, 3) = xFound.Address
.Cells(xRow, 4) = xFound.Value
End If
Set xFound = xWk.Cells.FindNext(After:=xFound)
Loop While xStrAddress <> xFound.Address
Next
xWb.Close (False)
xStrFile = Dir
Loop
Next
.Columns("A:D").EntireColumn.AutoFit
End With
ExitHandler:
Set xOut = Nothing
Set xWk = Nothing
Set xWb = Nothing
Set xFld = Nothing
Set xFso = Nothing
Application.ScreenUpdating = xUpdate
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
Resume ExitHandler
End Sub
I want the search result for all the search values in one sheet or csv.
I couldn't test my code fully, but from the little testing I did do, it seemed to work:
Option Explicit
Private Function GetFolderPath(ByRef folderPathSelected As String) As Boolean
Dim xFileDialog As FileDialog
Set xFileDialog = Application.FileDialog(msoFileDialogFolderPicker)
xFileDialog.AllowMultiSelect = False
xFileDialog.Title = "Select a folder"
If xFileDialog.Show = -1 Then
folderPathSelected = xFileDialog.SelectedItems(1)
GetFolderPath = True
End If
End Function
Private Function GetAllExcelFilesInFolder(ByVal someFolderPath As String, Optional ByVal dirCriteria As String = "*.xls*") As Collection
' Could probably use FileSystemObject instead for this.
Dim outputCollection As Collection
Set outputCollection = New Collection
If Right$(someFolderPath, 1) <> "\" Then
someFolderPath = someFolderPath & "\"
End If
Dim Filename As String
Filename = Dir$(someFolderPath & dirCriteria)
Do Until Len(Filename) = 0
outputCollection.Add someFolderPath & Filename
Filename = Dir$()
Loop
Set GetAllExcelFilesInFolder = outputCollection
End Function
Private Function MaybeUnion(ByVal firstRange As Range, ByVal secondRange As Range) As Range
' Assumes firstRange is good (and doesn't need checking).
If Not (secondRange Is Nothing) Then
Set MaybeUnion = Union(firstRange, secondRange)
Else
Set MaybeUnion = firstRange
End If
End Function
Private Function FindAllInWorkbook(ByVal someWorkbook As Workbook, _
ByVal What As String, _
Optional ByVal LookIn As XlFindLookIn = xlValues, _
Optional ByVal LookAt As XlLookAt = xlWhole, _
Optional SearchOrder As XlSearchOrder = xlByRows, _
Optional SearchDirection As XlSearchDirection = xlNext, _
Optional ByVal MatchCase As Boolean = False) As Range
Dim outputRange As Range
Dim targetSheet As Worksheet
For Each targetSheet In someWorkbook.Worksheets
Dim cellFound As Range
Set cellFound = targetSheet.Cells.Find(What, , LookIn, LookAt, LookAt, SearchOrder, SearchDirection, MatchCase)
If Not (cellFound Is Nothing) Then
Dim addressOfFirstMatch As String
addressOfFirstMatch = cellFound.Address
Do
Set outputRange = MaybeUnion(cellFound, outputRange)
Set cellFound = targetSheet.Cells.FindNext(After:=cellFound)
Loop Until cellFound.Address = addressOfFirstMatch
End If
Next targetSheet
Set FindAllInWorkbook = outputRange
End Function
Private Sub FindStringsInWorkbooks()
Dim folderPath As String
If Not GetFolderPath(folderPath) Then Exit Sub
Dim filePathsToProcess As Collection
Set filePathsToProcess = GetAllExcelFilesInFolder(folderPath)
Dim stringsToSearchFor As Variant
stringsToSearchFor = Array("search_a", "search_b", "search_c", "search_d", "search_e", "search_f")
Dim outputSheet As Worksheet
Set outputSheet = ThisWorkbook.Worksheets.Add
outputSheet.Range("A1:D1") = Array("Workbook", "Worksheet", "Cell", "Text in Cell")
Dim outputRowIndex As Long
outputRowIndex = 1 ' Skip header row
Dim filePath As Variant
For Each filePath In filePathsToProcess
Dim targetBook As Workbook
Set targetBook = Application.Workbooks.Open(Filename:=filePath, UpdateLinks:=0, ReadOnly:=True, AddToMRU:=False)
Dim stringToFind As Variant
For Each stringToFind In stringsToSearchFor
Dim cellsFound As Range
Set cellsFound = FindAllInWorkbook(targetBook, stringToFind, xlValues, xlWhole, xlByRows, xlNext, False)
If Not (cellsFound Is Nothing) Then
Dim cell As Range
For Each cell In cellsFound
outputRowIndex = outputRowIndex + 1
With outputSheet
.Cells(outputRowIndex, "A") = targetBook.Name
.Cells(outputRowIndex, "B") = cell.Parent.Name
.Cells(outputRowIndex, "C") = cell.Address
.Cells(outputRowIndex, "D") = cell.Value
End With
Next cell
Else
Debug.Print "No results found for '" & stringToFind & "' in workbook '" & targetBook.Name & "'."
End If
Next stringToFind
targetBook.Close SaveChanges:=False
Next filePath
End Sub
If wanted:
It's good to take advantage of functions/procedures in your code, so that the code is a bit easier to read.
Since you aren't making changes to the workbook/worksheets inside the loop, it should be okay for you to return all matches first and then process them later altogether (rather than processing them as you find them).
I think it makes sense to have the workbook loop on the outside, and the search term loop on the inside. Otherwise, you will be opening and closing the same workbook N times (where N is how ever many search terms you have). However, this does mean that the order of the output/results will be different.
You may need to re-implement formatting (e.g. auto-fit column widths and so on) -- and toggle Application.ScreenUpdating as necessary.

Search for a value and return the entire row in Excel

I have a macro to search for a value on different sheets. This works fine,
but the problem is that I want the value of the entire row, not just the value I'm looking for.
The code is as follows:
Sub SearchFolders()
Dim xFso As Object
Dim xFld As Object
Dim xStrSearch As String
Dim xStrPath As String
Dim xStrFile As String
Dim xOut As Worksheet
Dim xWb As Workbook
Dim xWk As Worksheet
Dim xRow As Long
Dim xFound As Range
Dim xStrAddress As String
Dim xFileDialog As FileDialog
Dim xUpdate As Boolean
Dim xCount As Long
On Error GoTo ErrHandler
Set xFileDialog = Application.FileDialog(msoFileDialogFolderPicker)
xFileDialog.AllowMultiSelect = False
xFileDialog.Title = "select folder"
If xFileDialog.Show = -1 Then
xStrPath = xFileDialog.SelectedItems(1)
End If
If xStrPath = "" Then Exit Sub
xStrSearch = "searched value"
xUpdate = Application.ScreenUpdating
Application.ScreenUpdating = False
Set xOut = Worksheets.Add
xRow = 1
With xOut
.Cells(xRow, 1) = "book"
.Cells(xRow, 2) = "sheet"
.Cells(xRow, 3) = "cell"
.Cells(xRow, 4) = "search value"
Set xFso = CreateObject("Scripting.FileSystemObject")
Set xFld = xFso.GetFolder(xStrPath)
xStrFile = Dir(xStrPath & "\*.xls*")
Do While xStrFile <> ""
Set xWb = Workbooks.Open(Filename:=xStrPath & "\" & xStrFile, UpdateLinks:=0, ReadOnly:=True, AddToMRU:=False)
For Each xWk In xWb.Worksheets
Set xFound = xWk.UsedRange.Find(xStrSearch)
If Not xFound Is Nothing Then
xStrAddress = xFound.Address
End If
Do
If xFound Is Nothing Then
Exit Do
Else
xCount = xCount + 1
xRow = xRow + 1
.Cells(xRow, 1) = xWb.Name
.Cells(xRow, 2) = xWk.Name
.Cells(xRow, 3) = xFound.Address
.Cells(xRow, 4) = xFound.Value
End If
Set xFound = xWk.Cells.FindNext(After:=xFound)
Loop While xStrAddress <> xFound.Address
Next
xWb.Close (False)
xStrFile = Dir
Loop
.Columns("A:D").EntireColumn.AutoFit
End With
MsgBox xCount & "Cells found", , "EA"
ExitHandler:
Set xOut = Nothing
Set xWk = Nothing
Set xWb = Nothing
Set xFld = Nothing
Set xFso = Nothing
Application.ScreenUpdating = xUpdate
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
Resume ExitHandler
End Sub
I need this to look for a value in different books and to return the information of the entire row where the sought value was found.
Based on my best guesses as to what you could mean by "I want the value of the entire row":
To access the entire row as a Range object
Dim rng As Range
Set rng = xFound.EntireRow
To create a variable (dimensioned (1 To 1, 1 To 16384)) containing the values of the entire row:
Dim rngValue As Variant
rngValue = xFound.EntireRow.Value
MsgBox rngValue(1, 20) ' will display the value from column T
To individually access certain columns from the row:
MsgBox xFound.EntireRow.Cells(1, "T") ' will display the value from column T
MsgBox xFound.EntireRow.Range("T1") ' will display the value from column T
To set certain destination cells to the value from certain cells on the found row:
'Copy values from columns A to T from original row to columns D to W of the destination
.Cells(xRow, 4).Range("A1:T1").Value = xFound.EntireRow.Range("A1:T1").Value
To simply find the row number on which the find occurred:
MsgBox xFound.Row

Dynamic search in files

I am building a small search with xl files.
I have an xl sheet with column A, and I want to iterate through each cell in this col, and then to grab this value.
Then iterate through all files in a folder that I defined to look in (and it's subfolders) and look for matching values in a specific column (A for example) in each file. when it match, it adds it to another sheet with all the results.
So I managed to iterate through all the files in the folder and subfolders, and look for a specific value that I defined.
THE PROBLEM is when I got to the dynamic part - when I run through all the values in the column of the source file, and not just for a specific value.
I post my code with the comments and also where I think the problem is...
Sub SearchFolders()
Dim fso As Object
Dim strSearch As String
Dim strFile As String
Dim wOut As Worksheet
Dim wbk As Workbook
Dim wks As Worksheet
Dim lRow As Long
Dim rFound As Range
Dim strFirstAddress As String
Dim oFolder, oSubfolder, oFile, queue As Collection
Dim HostFolder As String
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
'********************
'GET FIRST COL VALUES
'********************
Dim i As Long
Dim j As Long
Dim searchItem As Variant
strSearch = ""
For i = 1 To Rows.Count
If Not IsEmpty(Cells(i, 1).Value) Then
strSearch = strSearch & "," & Cells(i, 1).Value
End If
Next i
'MsgBox (strSearch)
searchItem = Split(strSearch, ",")
HostFolder = "C:\Users\a\Desktop\xl files min\temp"
On Error GoTo ErrHandler
Application.ScreenUpdating = False
Set wOut = Worksheets.Add
lRow = 1
With wOut
.Cells(lRow, 1) = "Workbook"
.Cells(lRow, 2) = "Worksheet"
.Cells(lRow, 3) = "Cell"
.Cells(lRow, 4) = "Text in Cell"
'now some iterations through subfolders and folders
Set fso = CreateObject("Scripting.FileSystemObject")
Set queue = New Collection
queue.Add fso.GetFolder(HostFolder)
Do While queue.Count > 0
Set oFolder = queue(1)
queue.Remove 1 'dequeue
For Each oSubfolder In oFolder.SubFolders
queue.Add oSubfolder 'enqueue
Next oSubfolder
strFile = Dir(oFolder & "\*.xls*")
Do While strFile <> ""
Set wbk = Workbooks.Open _
(Filename:=oFolder & "\" & strFile, _
UpdateLinks:=0, _
ReadOnly:=True, _
AddToMRU:=False)
For Each wks In wbk.Worksheets
For j = 1 To UBound(searchItem) 'start iterating through the column's values
strSearch = searchItem(j) '***********A PROBLAM IN HERE?:
'******************************IF I PUT THE ARRAY LIKE SO AND MATCH, IT GOT STUCK, THOUGH,
'IF I PUT THE A VALUE THAT I KNOW THAT MATCH AS STRING FOR EXAMPLE
'I CAN DO: strSearch = "bla" THEN IT DOES WORKS... BUT I NEED THE DYNAMIC COL VALUES :\
'******************************************************
'MAYBE HERE THE PROBLAM? IF THERE IS A MATCH IT GOT STUCK
Set rFound = wks.UsedRange.Find(strSearch, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False) 'HERE
'MsgBox (strSearch)
If Not rFound Is Nothing Then
strFirstAddress = rFound.Address
End If
Do
If rFound Is Nothing Then
Exit Do
Else
lRow = lRow + 1
.Cells(lRow, 1) = oFolder & "\" & strFile
.Cells(lRow, 2) = wks.Name
.Cells(lRow, 3) = rFound.Address & temp
.Cells(lRow, 4) = rFound.Value
End If
Set rFound = wks.Cells.FindNext(After:=rFound)
Loop While strFirstAddress <> rFound.Address
'End If
Next j
Next
wbk.Close (False)
strFile = Dir
Loop
Loop
.Columns("A:D").EntireColumn.AutoFit
End With
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox ("This code ran successfully in " & SecondsElapsed & " seconds -- " & j)
ExitHandler:
Set wOut = Nothing
Set wks = Nothing
Set wbk = Nothing
Set oFolder = Nothing
Set oSubfolder = Nothing
Set oFile = Nothing
Set queue = Nothing
Set fso = Nothing
Application.ScreenUpdating = True
Exit Sub
ErrHandler:
MsgBox Err.Description, vbExclamation
Resume ExitHandler
End Sub

Resources