Excel VBA: Search Folder and Sub-Folders for Part Numbers Listed in Excel - excel

Beginner in VBA so sorry if the code is bad.
What I wanted to achieve was to search a folder and its sub folders for the .dxf file of a part number listed in Column B, and return either a "Yes" or "No" depending on whether that .dxf file exist in that folder or its sub folders.
What I expected was that the code would begin with the first part number listed in B2, search the folder and sub folders for the .dxf file related, return a value, then move on to the next one, B3 then B4 and so on until the part numbers stop.
What it does is search a folder for all listed part numbers in Column B, returns all values, then searches a subfolder, returns all values (overriding the previous results) and so on until there are no more subfolders to search.
I feel like I'm close to getting the result I want but not sure where I've gone wrong.
Code is below:
Option Explicit
Dim FileSystem As Object
Dim HostFolder As String
Sub FindFile()
HostFolder = "C:\Users\Anyone\DXF\"
Set FileSystem = CreateObject("Scripting.FileSystemObject")
DoFolder FileSystem.GetFolder(HostFolder)
End Sub
Sub DoFolder(Folder)
Dim SubFolder
Dim Row As Integer
Dim Extension As String
Dim Continue As Boolean
For Each SubFolder In Folder.SubFolders
DoFolder SubFolder
Next
Dim File
For Each File In Folder.Files
Continue = True
Extension = ".DXF"
Row = 2
While Continue
If Len(Range("B" & CStr(Row)).Value) = 0 Then Exit Sub
If Len(Dir(Folder.Path & "\" & Range("B" & CStr(Row)).Value & "*" & Extension)) = 0 Then
Range("F" & CStr(Row)).Value = "No"
Else
Range("F" & CStr(Row)).Value = "Yes"
End If
Row = Row + 1
Wend
Next
End Sub

I would rethink the approach here. Maybe try a macro to manage the row loop and a function to manage the file lookup
First macro to loop through all file names in Column B and outputs if a file is found in Column C
Second macro to search a folder/subfolder path. If the file is found, the loop ends and returns TRUE. If a file is not found, the macro runs it's course and exists with FALSE
Note the function is just demonstrating the logic. You just need to take the logic you have to manage the folder loop and implement in this function
Sub File_Range()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim lr As Long, i As Long
lr = ws.Range("B" & ws.Rows.Count).End(xlUp).Row
For i = 2 To lr
If Not IsEmpty(ws.Range("B" & i)) Then
ws.Range("C" & i) = File_Exists(Range("B" & i))
End If
Next i
End Sub
Public Function File_Exists(Target As Range) As Boolean
'False until file is found
File_Exists = False
For Each SubFolder In Folder.SubFolders
If Len(Dir(Folder.Path & "\" & Target.Value & "*" & Extension)) Then
File_Exists = True
Exit Function
End If
Next SubFolder
End Function

Check Files for Existence
The Basics
Basically (inaccurately), the following writes all file paths of the required (DXF) files found in the supplied folder path to an array, replaces them with file names without file extension and checks the values (file names) in the source column against the array and finally writes the required results (Yes/No) to the destination column.
Adjust the values in the constants section.
Only run checkFiles, the rest is being called by it.
Additional Functionality
To better understand the flow, in the Immediate window CTRL+G, you can monitor what is happening, if you uncomment the Debug.Print sections. Note that Join will fail if error values, but you can always create a loop like it is done for mData which possibly (probably) contains error values.
It will allow different worksheets for source and destination data.
If will allow source and destination data starting in different rows.
The Code
Option Explicit
Sub checkFiles()
Const FolderPath As String = "C:\Users\Anyone\DXF"
Const FileExt As String = "DXF" ' Not case-sensitive i.e. 'DXF = dxf'
Const fFound As String = "Yes"
Const fNotFound As String = "No"
Const srcName As String = "Sheet1"
Const srcFirst As String = "B2"
Const dstName As String = "Sheet1"
Const dstFirst As String = "F2"
Dim wb As Workbook
Set wb = ThisWorkbook
Dim rng As Range ' (Source and Destination) Data Range
Dim Data As Variant ' (Source and Destination) Data Array
Dim fData() As String ' File Data Array
Dim mData As Variant ' Match Data Array
Dim n As Long ' File Data and (Match) Data Array Elements Counter
' Write values from Source Range to Data Array.
Set rng = defineColumnRange(defineRange(wb.Worksheets(srcName), srcFirst))
Data = getColumn(rng)
'Debug.Print "Source Data:" & vbLf & Join(Application.Transpose(Data), vbLf)
' Write file paths to File Data Array.
fData = getFilePaths(FolderPath, "*." & FileExt)
'Debug.Print "File Data - File Paths:" & vbLf & Join(fData, vbLf)
' Replace file paths with file names without file extension.
For n = LBound(fData) To UBound(fData)
fData(n) = FileFromPath(fData(n), True) ' 'True' means no extension.
Next n
'Debug.Print "File Data - File Names:" & vbLf & Join(fData, vbLf)
' Write 'matches' to Match Data Array.
mData = Application.Match(Data, fData, 0)
'Debug.Print "Match Data:"
'For n = 1 To UBound(mData)
' Debug.Print mData(n, 1)
'Next
' Overwrite values in Data Array with 'matching results'.
For n = 1 To UBound(Data) ' or 'UBound(mData)'
If IsNumeric(mData(n, 1)) Then
Data(n, 1) = fFound
Else
Data(n, 1) = fNotFound
End If
Next n
'Debug.Print "Destination Data:" & vbLf _
& Join(Application.Transpose(Data), vbLf)
' Write values from Data Array to Destination Range.
With defineRange(wb.Worksheets(dstName), dstFirst)
Dim RowOffset As Long: RowOffset = .Row - rng.Row
Dim ColumnOffset As Long: ColumnOffset = .Column - rng.Column
Set rng = .Worksheet.Range(rng.Offset(RowOffset, ColumnOffset).Address)
End With
rng.Value = Data
End Sub
Function defineRange( _
ws As Worksheet, _
ByVal RangeAddress As String) _
As Range
On Error Resume Next
Set defineRange = ws.Range(RangeAddress)
On Error GoTo 0
End Function
Function defineColumnRange( _
FirstCell As Range) _
As Range
If Not FirstCell Is Nothing Then
With FirstCell
Dim rng As Range
Set rng = .Resize(.Worksheet.Rows.Count - .Row + 1)
Set rng = rng.Find("*", , xlFormulas, , , xlPrevious)
If Not rng Is Nothing Then
Set defineColumnRange = .Resize(rng.Row - .Row + 1)
End If
End With
End If
End Function
Function getColumn( _
rng As Range) _
As Variant
If Not rng Is Nothing Then
If InStr(rng.Address, ":") > 0 Then
getColumn = rng.Value
Else
Dim Data As Variant
ReDim Data(1 To 1, 1 To 1)
Data(1, 1) = rng.Value
getColumn = Data
End If
End If
End Function
Function getFilePaths( _
ByVal FolderPath As String, _
Optional ByVal FilePattern As String = "") _
As Variant
Dim ExecString As String
ExecString = "cmd /c Dir """ & FolderPath & Application.PathSeparator _
& FilePattern & """ /b/s"
getFilePaths = Filter(Split(CreateObject("WScript.Shell") _
.Exec(ExecString).StdOut.ReadAll, vbCrLf), ".") ' 'vbCrLf' is a must.
End Function
Function FileFromPath( _
ByVal FilePath As String, _
Optional ByVal NoExtension As Boolean = False) _
As String
Dim FileName As String
FileName = Right(FilePath, _
Len(FilePath) - InStrRev(FilePath, "\"))
If NoExtension Then
FileName = Left(FileName, InStrRev(FileName, ".") - 1)
End If
FileFromPath = FileName
End Function

Related

Creating Automatic Folders based on excel list

I am using this code to create folders based on names mentioned in Column A, however at times this does not create folders and at times it does not create all the folders. I could not figure out the issue or if anything is missing in it.
I will really appreciate if any amendment could be made where if a particular folder is already available (based on cell value) it does not show error.
Sub MakeFolders()
Dim Rng As Range
Dim maxRows, maxCols, r, c As Integer
Set Rng = Selection
maxRows = Rng.Rows.Count
maxCols = Rng.Columns.Count
For c = 1 To maxCols
r = 1
Do While r <= maxRows
If Len(Dir(ActiveWorkbook.Path & "\" & Rng(r, c), vbDirectory)) = 0 Then
MkDir (ActiveWorkbook.Path & "\" & Rng(r, c))
On Error Resume Next
End If
r = r + 1
Loop
Next c
End Sub
Please, try the next adapted code. It uses an array, all iteration being done in memory (much faster than iterating between cells) and checks if a cell is empty or contains illegal characters, not accepted in a path:
Sub MakeFolders()
Dim sh As Worksheet, lastR As Long, arr, i As Long, rootPath As String
Set sh = ActiveSheet
lastR = sh.Range("A" & sh.rows.count).End(xlUp).row
arr = sh.Range("A2:A" & lastR).Value2
rootPath = ThisWorkbook.Path & "\"
For i = 1 To UBound(arr)
If arr(i, 1) <> "" And noIllegalChars(CStr(arr(i, 1))) Then
If Dir(rootPath & arr(i, 1), vbDirectory) = "" Then
MkDir rootPath & arr(i, 1)
End If
Else
MsgBox "Illegals characters or empty cell (" & sh.Range("A" & i + 1).address & ")..."
End If
Next i
End Sub
Function noIllegalChars(x As String) As Boolean
Const illCh As String = "*[\/\\"":\*?]*"
If Not x Like illCh Then noIllegalChars = True
End Function
It iterates between (existing) cells in column A:A and check if they are empty, do not contain illegal characters or the folder has already been created.
Create Folders From Range Selection
This solution creates folders simply if it is possible i.e. based on On Error Resume Next making it kind of a hack.
To 'make amends' on the hack part, it returns a table, containing some stats about the folders that could not be created, in the Immediate window (Ctrl+G).
If you're not interested at all in why a folder was not created, remove the Debug Print routine i.e. the lines ending in ' DP.
Option Explicit
Sub CreateFoldersFromSelection()
If Selection Is Nothing Then Exit Sub
If Not TypeOf Selection Is Range Then Exit Sub
' Set the workbook...
Dim wb As Workbook: Set wb = Selection.Worksheet.Parent
' ... to build the path.
Dim fPath As String: fPath = wb.Path & Application.PathSeparator
Dim arg As Range, Data() As Variant
Dim r As Long, c As Long, rCount As Long, cCount As Long
Dim ErrNum As Long, ErrDescription As String ' DP
Debug.Print "Folders in '" & fPath & "' not created:" ' DP
Debug.Print "Name", "Cell Address", "Error Number", "Error Description" ' DP
' Loop over each area of the selection...
For Each arg In Selection.Areas
' ... to return the area's values in an array, ...
rCount = arg.Rows.Count
cCount = arg.Columns.Count
If rCount * cCount = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = arg.Value
Else
Data = arg.Value
End If
' ... then loop over the values in the array...
For r = 1 To rCount
For c = 1 To cCount
' ... to attempt to create the current folder.
On Error Resume Next
MkDir fPath & Data(r, c)
ErrNum = Err.Number ' DP
ErrDescription = Split(Err.Description, vbLf)(0) & "..." ' DP
On Error GoTo 0
If ErrNum <> 0 Then ' DP
' Print a line of stats about the folder not created.
Debug.Print Data(r, c), arg.Cells(r, c).Address(0, 0), _
ErrNum, ErrDescription ' DP
End If ' DP
Next c
Next r
Next arg
MsgBox "Folders created.", vbInformation
End Sub

Efficient Use of For Loop

I have a fairly large excel file (Think 65,000+ rows).
Within the excel file, only two columns matter for this exercise: CCNumber and FileFound (Col BC/BD).
I am trying to use a for loop to loop through the 65,000 + rows and compare the CCNumber (ID) against a folder of files (30,000 files), and then if an id matches/isnt found print "Available" or "Not Found" in the FileFound column - As below:
Sub LoopFiles
Dim fileName As Variant, csheet As Variant
fileName = Dir("Some\Directory\Here\*pdf")
Dim CCNums As Range
Set CCNums = Range("BC4:BC68512")
Application.ScreenUpdating = False
While fileName <> ""
ID = Left(fileName,6) 'id is a 6 digit numeric number, strip away everything else
For Each CCNum in CCNums
csheet = Left(CCNum, 6)
if(ID = csheet) Then
CCNum.Offset(0,1).Value = "Available"
Else
CCNum.Offset(0,1).Value = "Not Found"
End If
Next CCNum
fileName = Dir
Wend
Application.ScreenUpdating = True
End Sub
The above is hilariously inefficient and it takes forever. Is there a way I can speed this up, or am I just going to have to sit here and wait for the spinning wheel of doom to stop.
Instead of looping through a file list you can directly check with Dir and wildcards if a file exists.
eg. you can use Dir("C:\Temp\myNumber*.pdf") to find a file that is named myNumberAndUnusefulText.pdf. So if you use fileName = Dir("Some\Directory\Here\" & CSheet & "*.pdf") it will return the file name of a file that starts with the number in CSheet.
Further reading all the values into an array first and then processing the array makes your code much faster. Reading and writing actions to cells use a lot of overhead and therefore are slow. By reading the values into an array you reduce it to just one cell reading and one cell writing action.
Option Explicit
Public Sub LoopFilesImproved()
Dim CCNums As Range
Set CCNums = ThisWorkbook.Worksheets("Sheet1").Range("BC4:BC68512") ' always specify in which sheet a range is!
' define output range
Dim Output As Range
Set Output = CCNums.Offset(ColumnOffset:=1)
' read output range into array for faster processing
Dim OutputValues() As Variant
OutputValues = Output.Value2
' read all values into an array for faster processing
Dim CCNumsValues() As Variant
CCNumsValues = CCNums.Value2
' loop through numbers and check if a file exists
Dim iCCNum As Long
For iCCNum = LBound(CCNumsValues, 1) To UBound(CCNumsValues, 1)
Dim CSheet As String
CSheet = Left$(CCNumsValues(iCCNum, 1), 6)
Dim fileName As String
fileName = Dir("Some\Directory\Here\" & CSheet & "*.pdf")
If fileName <> vbNullString Then
OutputValues(iCCNum, 1) = "Available"
Else
OutputValues(iCCNum, 1) = "Not Found"
End If
Next iCCNum
' write array values back to cell
Output.Value2 = OutputValues
End Sub
You can try first collecting all of the file names into a dictionary - from that point the check will be fast...
Sub LoopFiles()
Dim dictFiles As Object, arrCC, arrAv, rngCC As Range, r As Long
Set dictFiles = FileIds("Some\Directory\Here\*.pdf") 'collect all the file Id's
Set rngCC = ActiveSheet.Range("BC4:BC68512")
arrCC = rngCC.Value
ReDim arrAv(1 To UBound(arrCC, 1), 1 To 1) 'size the "available?" array
For r = 1 To UBound(arrCC, 1) 'loop data from BC
id = Left(arrCC(r, 1), 6) 'extract the id
arrAv(r, 1) = IIf(dict.exists(id), "Available", "Not found")
Next r
rngCC.Offset(0, 1).Value = arrAv 'populate availability in BD
End Sub
'scan all files matching the `folderPath` pattern, and return a Dictionary object
' with keys equal to the first 6 characters of the file names
Function FileIds(folderPath As String)
Dim dict As Object, f, id
Set dict = CreateObject("scripting.dictionary")
f = Dir(folderPath)
Do While Len(f) > 0
If Len(f) >= 10 Then dict(Left(f, 6)) = True 'need at least 10 chars with the extension
f = Dir()
Loop
Set FileIds = dict
End Function
In a quick test on a local drive with 30k files, calling FileIds took about 0.08 seconds. Calling Dir() 65k times on the same folder took 12-13secs.
Update File Availability Using a List
Option Explicit
Sub UpdateFilesAvailability()
Const FolderPath As String = "C:\Test"
Const RightFilePart As String = "*.pdf"
Const idLen As Long = 6
Const sRangeAddress As String = "BC4:BC68512"
Const dCol As String = "BD"
Const dYes As String = "Available"
Const dNo As String = "Not found"
Const Msg As String = "Files availability updated."
' Validate the folder path.
Dim fPath As String: fPath = FolderPath
If Right(fPath, 1) <> "\" Then fPath = fPath & "\"
If Len(Dir(fPath, vbDirectory)) = 0 Then
MsgBox "The folder '" & fPath & "' doesn't exist.", vbCritical
Exit Sub
End If
' Reference the worksheet ('ws').
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
' Reference the source range ('srg').
Dim srg As Range: Set srg = ws.Range(sRangeAddress)
' Write the values from the source range
' to a 2D one-based one-column array ('Data').
Dim Data As Variant: Data = srg.Value
Dim cString As String ' Current String
Dim fName As String ' Current File Name
Dim r As Long ' Current Array Row
Dim FileFound As Boolean
' Loop through the rows of the destination array and replace its values
' with the results.
For r = 1 To UBound(Data, 1)
cString = CStr(Data(r, 1))
If Len(cString) >= idLen Then
fName = Dir(fPath & Left(cString, idLen) & RightFilePart)
If Len(fName) > 0 Then FileFound = True
End If
If FileFound Then
Data(r, 1) = dYes
FileFound = False
Else
Data(r, 1) = dNo
End If
Next r
' Reference the destination range.
Dim drg As Range: Set drg = srg.EntireRow.Columns(dCol)
' Write the values from the array to the destination range.
drg.Value = Data
'drg.EntireColumn.AutoFit
'ws.Parent.Save ' save the workbook
MsgBox Msg, vbInformation
End Sub

How to efficiently create csv-files for each column in a worksheet?

I have a worksheet with many columns (82 in my case) and I am looking to create a csv-file for each column. I manage to do it with the below code, thanks to the help of many questions/answers on this site. Running the code gives some action on the windows taskbar I have not seen before (the creation and closing of the files) but I have the feeling there is a more efficient and faster way. Any suggestions?
' Create a separate csv file for each column.
Sub ColumnsToCSV()
Dim i As Byte
Dim cols As Byte ' column count
Dim name As String ' 01, 02, .., 99
cols = ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell).Column ' count columns
For i = 1 To cols ' loop columns
name = Format(i, "00") ' 1 => 01, etc.
Sheets.Add(After:=Sheets(Sheets.Count)).name = name ' add sheet
Sheets("Data").Columns(i).Copy Destination:=Sheets(name).Columns(1) ' copy data
ThisWorkbook.Sheets(name).Copy ' create copy
ActiveWorkbook.SaveAs Filename:=name, FileFormat:=xlCSV ' save to csv
ActiveWorkbook.Close ' close csv
Application.DisplayAlerts = False ' disable alerts
ActiveSheet.Delete ' delete sheet
Application.DisplayAlerts = True ' enable alerts
Next i
End Sub
Try this out:
' Create a separate csv file for each column.
Sub ColumnsToCSV()
Dim name As String, pth As String, cols As Long, i As Long
Dim rng As Range, data, ws As Worksheet, r As Long, v
Set ws = ActiveSheet
cols = ws.UsedRange.SpecialCells(xlCellTypeLastCell).Column
pth = ThisWorkbook.Path & "\" 'or whereever you want to save....
For i = 1 To cols
data = AsArray(ws.Range(ws.Cells(1, i), ws.Cells(Rows.Count, i).End(xlUp)))
For r = 1 To UBound(data, 1)
v = data(r, 1)
If InStr(v, ",") > 0 Then data(r, 1) = """" & v & """" 'quote commas
Next r
'write the output (note Tanspose() has a limit of approx 63k items)
PutContent pth & Format(i, "00") & ".csv", _
Join(Application.Transpose(data), vbCrLf)
Next i
End Sub
'write text to a file
Sub PutContent(f As String, content As String)
CreateObject("scripting.filesystemobject"). _
opentextfile(f, 2, True).write content
End Sub
'return range value as array (handle case where range is a single cell)
Function AsArray(rng As Range)
Dim rv()
If rng.Cells.Count = 1 Then
ReDim rv(1 To 1, 1 To 1)
rv(1, 1) = rng.Value
AsArray = rv 'edit: this was missing...
Else
AsArray = rng.Value
End If
End Function

How to find matching headers in a worksheet using VBA?

There is a sheet with headers. Out of those headers I have to verify/check that 12 headers are available. If they are available message should show as success and if not it should show that the specific header is missing.
I created a sub and took an array with those twelve values but how to match?
Please test the next code. You will build the headers array in a way to reflect your reality:
Sub testCheckHeadersArray()
Dim sh As Worksheet, arrH As Variant, El As Variant, C As Range
Dim boolFound As Boolean, strNotFound As String, lastCol As Long
arrH = Split("Header1,Header3,Header4,Header5,Header6,Header7,Header8,Header9,Header10", ",")
Set sh = ActiveSheet 'please, use here your sheet to be checked
lastCol = sh.Cells(1, Cells.Columns.Count).End(xlToLeft).column
For Each El In arrH
boolFound = False
For Each C In sh.Range(sh.Cells(1, 1), sh.Cells(1, lastCol))
If UCase(El) = UCase(C.value) Then
boolFound = True: Exit For
End If
Next
If Not boolFound Then strNotFound = strNotFound & El & vbCrLf
Next
If strNotFound <> "" Then
MsgBox "The next headers have not been found:" & vbCrLf & strNotFound
Else
MsgBox "Everything OK"
End If
End Sub
If you have a sheet with the correct headers, you can extract the array from there:
Set shH = Worksheets("HeaderModel")
arrH = shH.Range(Range("A1"), shH.Cells(1, shH.Cells(1, _
Cells.Columns.Count).End(xlToLeft).column)).value
This is how you might tackle your problem. Please read the comments in the code to understand how it's done.
Option Explicit
Sub TestHeaderPresence()
Dim CheckHeaders As Variant
Dim Headers As String
' list the required headers
Headers = "Header1,Header3,Header4,Header5,Header6,Header7,Header8," & _
"Header9,Header10,Header11,Header12"
' pass the list to the function
CheckHeaders = HeadersArePresent(Headers)
If CheckHeaders = True Then
MsgBox "All headers are present.", vbInformation, "Caption check"
Else
MsgBox "At least caption """ & CheckHeaders & """" & " is missing.", _
vbInformation, "Caption check"
End If
End Sub
Function HeadersArePresent(Headers As String) As Variant
Dim Fun As String ' function return
Dim Captions() As String
Dim HeaderRange As Range
Dim HeaderArray As Variant
Dim Tmp As Variant
Dim i As Long
With ActiveSheet ' replace with "With Worksheets("[tab name]")"
' Available Captions start from column "C" in row "1"
' modify as appropriate
Tmp = .Range(.Cells(1, "C"), .Cells(1, .Columns.Count).End(xlToLeft)).Value
End With
ReDim HeaderArray(1 To UBound(Tmp, 2))
For i = 1 To UBound(Tmp, 2)
HeaderArray(i) = Tmp(1, i)
Next i
HeaderArray = Join(HeaderArray, ",")
Captions = Split(Headers, ",")
For i = 0 To UBound(Captions)
If InStr(HeaderArray, Captions(i)) = 0 Then
Fun = Captions(i)
Exit For
End If
Next i
' return True or the name of first missing header
HeadersArePresent = IIf(Len(Fun), Fun, True)
End Function
Approach via Filter() function
After defining the set of regular headers (~> [0]) and current headers (~> [1]), the Filter() function allows to reduce the initially full set of headers subsequently by the next current header via the 3rd include argument set to False (~> section [2]).
See MS Help: If include is False, Filter returns the subset of the array that does not contain match as a substring
Sub ListMissingHeaders()
'[0] define needed headers and assign them to 1-dim array
Const HEADERLIST = "Header1,Header3,Header4,Header5,Header6,Header7,Header8,Header9,Header10"
Dim regularHeaders: regularHeaders = Split(HEADERLIST, ",")
'[1] get current headers
With Sheet1 ' << change to actual sheet's Code(Name)
Dim lastCol As Long ' get last column in head line
lastCol = .Cells(1, .Cells.Columns.Count).End(xlToLeft).Column
Dim currHeaders ' assign current headers to 2-dim array
currHeaders = .Range("1:1").Resize(columnsize:=lastCol)
End With
'[2] Filter regular set of headers passed to array missingHeaders
Dim i As Long, missingHeaders
missingHeaders = regularHeaders ' start with complete set of headers
For i = 1 To UBound(currHeaders, 2) ' filter out existing headers one by one
missingHeaders = Filter(missingHeaders, currHeaders(1, i), False, vbTextCompare)
Next i
'[3] show missing headers
Debug.Print UBound(missingHeaders) + 1 & " missing Headers: """ & _
Join(missingHeaders, """, """)
End Sub

How do I, modify vba code to repeat until finished?

I found a piece of vba code that counts the number of xml files in a specific folder, i modified it to only count files with a specific string (located in cell F3) as part of the file name. And print that count in the adjacent cell (cell G3). Its working as expected.
My problem is that I need the results for a entire range. The range of strings are created from a pivot table.
I tried just dublicating the code and modifying the reference cells, and that works. But the range can be from a few strings to 70+, i'm sure that there is a much more efficient and cleaner way than running the code 70+ times.
Dim FolderPath As String, path As String, count As Integer
FolderPath = "C:\Test\PJC"
path = FolderPath & "\*" & Range("F3") & "*.xml"
FileName = Dir(path)
Do While FileName <> ""
count = count + 1
FileName = Dir()
Loop
Range("G3").Value = count
There you have alternatives:
' One line solution, but can count all files only, without wildcards
Debug.Print CreateObject("Scripting.FileSystemObject").GetFolder("C:\temp\").Files.count
' Counting all *.pdf files in C:\temp
FolderPath = "\\temp\\"
Path = "pdf"
Dim objWMIService As Object
Dim objFiles As Object
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set objFiles = objWMIService.ExecQuery("Select * from CIM_DataFile where Path = '" & FolderPath & "' and Extension = '" & Path & "'")
Debug.Print "Files count: ", objFiles.Count
Iterating over a Range can be achieved as follows:
Sub IterateRange()
Dim rng As Range, cell As Range
Dim myWs As Worksheet
Set myWs = Application.Worksheets("sheet1")
Set rng = myWs.Range("A1:A6")
For Each cell In rng
MsgBox cell.Value
Next cell
End Sub
Often iterating might be more straightforward using the .Cells property of a Worksheet, it could look like this:
Sub IterateRange()
Dim i As Long
Dim myWs As Worksheet
Set myWs = Application.Worksheets("Sheet1")
for i=1 to 6
MsgBox myWs.Cells(i, 1)
next i
End Sub
For your example, if you have the strings you want to check for in the Range F3:F73, and the count results should be in the Range G3:G73 the loop you are looking for might look something like this:
Sub Example()
Dim FolderPath As String, path As String, count As Integer, i As Integer
Dim myWs As Worksheet
Set myWs = Application.Worksheets("Sheet1")
For i = 3 To 73
count=0
FolderPath = "C:\Test\PJC"
path = FolderPath & "\*" & myWs.Cells(i, 6) & "*.xml"
Filename = Dir(path)
Do While Filename <> ""
count = count + 1
Filename = Dir()
Loop
myWs.Cells(i, 7) = count
Next i
End Sub
For the loop to stop automatically after the last row of input strings just add
if myWs.Cells(i, 6)="" then
exit for
end if
between the lines For i = 3 To 73 and count=0
Guess this will do the trick.
I took your code and put it in a For loop.
I define the start row as row 3.
EndRange will search for the last value in column F. The For loop will iterate from StartRange to EndRange values.
I also made the count to print the the result from the count variable for each loop in the G column.
Sub LoopRange()
Dim FolderPath As String, path As String, count As Integer
Dim StartRange As Long, EndRange As Long
StartRange = 3 'Row = 3 for start
EndRange = Cells(Rows.count, "F").End(xlUp).Row 'Find last row in column F
For i = StartRange To EndRange 'Loop from start to end value
FolderPath = "C:\Test\PJC"
path = FolderPath & "\*" & Range(Cells(i, "F"), Cells(i, "F")) & "*.xml" 'Take the value from row i and column F in the loop.
Filename = Dir(path)
Do While Filename <> ""
count = count + 1
Filename = Dir()
Loop
Range(Cells(i, "G"), Cells(i, "G")).Value = count 'Print the count
Next i
End Sub

Resources