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
Related
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.
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
I run a machine which puts out multiple text files which I plot, I have a VBA script which can import all of the files I want to plot from a folder and puts them on their own sheet. I was wondering if there was a way in which I could automatically have them plotted when they are imported as well? I need a separate graph for each pair of tests. That is I have "Test A-1" and "Test A-2" which are plotted against each other, "Test B-1" and "Test B-2" on a new graph etc. Sorry if this is confusing, I am still pretty new to VBA and would love a tool like this to make my life a bit easier. I have included my code which does all the importing below. Each text file (which is just data for a x-y scatter plot) is then given its own sheet, with the data in columns A and B. Apologies in advance for the terrible formatting, I didn't write it!
Sub ImportTextToExcel()
'UpdatebyExtendoffice20180911
Dim xWb As Workbook
Dim xToBook As Workbook
Dim xStrPath As String
Dim xFileDialog As FileDialog
Dim xFile As String
Dim xFiles As New Collection
Dim I As Long
Dim xIntRow As Long
Dim xFNum, xFArr As Long
Dim xStrValue As String
Dim xRg As Range
Dim xArr
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
If Right(xStrPath, 1) <> "\" Then xStrPath = xStrPath & "\"
xFile = Dir(xStrPath & "*.txt")
If xFile = "" Then
MsgBox "No files found", vbInformation
Exit Sub
End If
Do While xFile <> ""
xFiles.Add xFile, xFile
xFile = Dir()
Loop
Set xToBook = ThisWorkbook
On Error Resume Next
Application.ScreenUpdating = False
If xFiles.Count > 0 Then
For I = 1 To xFiles.Count
Set xWb = Workbooks.Open(xStrPath & xFiles.Item(I))
xWb.Worksheets(1).Copy after:=xToBook.Sheets(xToBook.Sheets.Count)
ActiveSheet.Name = xWb.Name
xWb.Close False
xIntRow = ActiveCell.CurrentRegion.Rows.Count
For xFNum = 1 To xIntRow
Set xRg = ActiveSheet.Range("A" & xFNum)
xArr = Split(xRg.Text, " ")
If UBound(xArr) > 0 Then
For xFArr = 0 To UBound(xArr)
If xArr(xFArr) <> "" Then
xRg.Value = xArr(xFArr)
Set xRg = xRg.Offset(ColumnOffset:=1)
End If
Next
End If
Next
Next
End If
Application.ScreenUpdating = True
End Sub
Option Explicit
Sub ImportTextToExcel()
'UpdatebyExtendoffice20180911
Dim xWb As Workbook, xToBook As Workbook, ws As Worksheet
Dim xFile As String, xStrPath As String, xStrValue As String
Dim xRg As Range, cht As Chart
Dim xFiles As New Collection
Dim i As Long
' choose folder
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
.Title = "Select a folder"
If .Show = -1 Then
xStrPath = .SelectedItems(1)
End If
End With
If xStrPath = "" Then
Exit Sub
ElseIf Right(xStrPath, 1) <> "\" Then
xStrPath = xStrPath & "\"
End If
' build collection of text files
xFile = Dir(xStrPath & "*.txt")
Do While xFile <> ""
xFiles.Add xFile, xFile
xFile = Dir()
Loop
If xFiles.Count = 0 Then
MsgBox "No files found", vbCritical
Exit Sub
End If
' import data
Set xToBook = ThisWorkbook
Application.ScreenUpdating = False
For i = 1 To xFiles.Count
' import data into new sheet
Set xWb = Workbooks.Open(xStrPath & xFiles.Item(i))
xWb.Worksheets(1).Copy after:=xToBook.Sheets(xToBook.Sheets.Count)
Set ws = ActiveSheet
ws.Name = xWb.Name
xWb.Close False
' split on space
Set xRg = ws.Range("A1")
xRg.CurrentRegion.TextToColumns Destination:=xRg.Cells(1, 1), _
DataType:=xlDelimited, _
ConsecutiveDelimiter:=True, _
Space:=True, FieldInfo:=Array(Array(1, 1), Array(2, 1))
' create chart
Set cht = ws.Shapes.AddChart.Chart
With cht
.ChartType = xlXYScatter
.SetSourceData Source:=xRg.CurrentRegion
End With
Next
Application.ScreenUpdating = True
MsgBox xFiles.Count & " files imported", vbInformation
End Sub
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.
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