VBA Trying to search for string, use range to copy the information in that column's values and output it to a spreadsheet - vba

I have many Excel documents (containing about a page of information, A-H columns and 1-25rows give or take a few) in a folder titled "Progress".
In one Excel document, I am trying to search for a particular column title "Tool Cutter" and take everything listed in that column, copy it, and output it to a separate spreadsheet (all of the tools are separated by a semicolon if that helps at all).
I am trying to write a program which goes into the "Progress" folder and will loop through opening each file, copying the "Tool Cutter" values I need, outputing it to a separate Excel spreadsheet I've titled "MasterList.xlsm", closing the file, and working through all of the files in that folder until there are none left.
It would be helpful if the "MasterList.xlsm" file could have Name in column 1 and Tools in column 2.
Any advice would be very helpful! I am not an expert in VBA.
What I have been trying:
Methods with AdvancedFilter, CopyToRange, SearchString...
All of the information I am trying to grab is in a column between titles "tools" and "general setup" so this code has been somewhat helpful:
Sub LoopThroughDirectory()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim MyFolder As String
Dim Sht As Worksheet
Dim i As Integer
Dim LastRow As Integer, erow As Integer
'Speed up process by not updating the screen
'Application.ScreenUpdating = False
MyFolder = "C:\Users\trembos\Documents\TDS\progress\"
Set Sht = ActiveSheet
'create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'get the folder object
Set objFolder = objFSO.GetFolder(MyFolder)
i = 1
'loop through directory file and print names
For Each objFile In objFolder.Files
If LCase(Right(objFile.Name, 3)) <> "xls" And LCase(Left(Right(objFile.Name, 4), 3)) <> "xls" Then
Else
'print file name
Sht.Cells(i + 1, 1) = objFile.Name
i = i + 1
Workbooks.Open fileName:=MyFolder & objFile.Name
End If
'Range("J1").Select
'Selection.Copy
'Windows("masterfile.xlsm").Activate
'Range("D2").Select
'ActiveSheet.Paste
ActiveWorkbook.Close SaveChanges:=False
Next objFile
'Application.ScreenUpdating = True
End Sub
**The document (image attached) is not formatted them same way every time, depending on the info available sometimes column number or row number is different.

I would use this to get the last row/column - much faster than looping:
Function getLastRow(sheet As String, Col As Variant) As Integer
getLastRow = Sheets(sheet).Cells(Sheets(sheet).Rows.Count, Col).End(xlUp).Row
End Function
Function getLastCol(sheet As String, row As Variant) As Integer
getLastCol = Sheets(sheet).Cells(row, Sheets(sheet).Columns.Count).End(xlToLeft).Column
End Function
The full version of these functions lets you specify the workbook to check
(which is important when you are opening multiple workbooks like you plan to)
Function GetLastCol(Row As Variant, Optional Sheet As String, Optional WB As Variant) As Integer
If IsMissing(WB) Then
If Sheet = vbNullString Then
GetLastCol = Cells(Row, Columns.Count).End(xlToLeft).Column
Else
GetLastCol = Sheets(Sheet).Cells(Row, Sheets(Sheet).Columns.Count).End(xlToLeft).Column
End If
Else
If Sheet = vbNullString Then
GetLastCol = WB.ActiveSheet.Cells(Row, WB.ActiveSheet.Columns.Count).End(xlToLeft).Column
Else
GetLastCol = WB.Sheets(Sheet).Cells(Row, WB.Sheets(Sheet).Columns.Count).End(xlToLeft).Column
End If
End If
End Function
Function GetLastRow(Col As Variant, Optional Sheet As String, Optional WB As Variant) As Integer
If IsMissing(WB) Then
If Sheet = vbNullString Then
GetLastRow = Cells(Rows.Count, Col).End(xlUp).Row
Else
GetLastRow = Sheets(Sheet).Cells(Sheets(Sheet).Rows.Count, Col).End(xlUp).Row
End If
Else
If Sheet = vbNullString Then
GetLastRow = WB.ActiveSheet.Cells(WB.ActiveSheet.Rows.Count, Col).End(xlUp).Row
Else
GetLastRow = WB.Sheets(Sheet).Cells(WB.Sheets(Sheet).Rows.Count, Col).End(xlUp).Row
End If
End If
End Function
If you have a square block of data like most excel sheets, you can also use:
ActiveSheet.UsedRange.Rows.Count
ActiveSheet.UsedRange.Columns.Count
The benefit of using these functions is that you can use it like:
Range("A1:A" & GetLastRow("A"))
The rest of your find code looks okay.
Here is a function to find all files in a folder.
It returns a collection of path names that you can iterate through using a For Each loop as demonstrated below:
Private Function GetFiles(Path As String, Optional Extension As String = "*") As Collection
Dim objFSO As Object
Dim FilesReturned As Collection
Set FilesReturned = New Collection
Dim Files, File
Set objFSO = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
Set Files = objFSO.GetFolder(Path).Files
On Error GoTo 0
If Files Is Nothing Then Exit Function
For Each File In Files
If UCase(objFSO.GetExtensionName(Path & File.Name)) Like UCase(Replace(Extension, ".", "")) Then
FilesReturned.Add (Path & IIf(Right(Path, 1) = "\", "", "\") & File.Name)
End If
Next File
Set GetFiles = FilesReturned
End Function
You can use this with a For Each loop to loop through each workbook.
You can open them using Workbooks.Open and use your find code like so:
(This code should go in the master sheet)
Sub GetTools()
Dim Files as Collection
On Error Resume Next
Set Files = GetFiles("C:\OurPath")
On Error GoTo 0
If Files Is Nothing Then
MsgBox ("No Files Found!")
Exit Sub
End If
'You can also use this to specify the extension if there are other types:
'Set Files = GetFiles("C:\OurPath","xls")
Dim ThisWb as Workbook
Set ThisWb = ThisWorkbook
Application.ScreenUpdating = False
For Each File In Files
Workbooks.Open File, ReadOnly:=True
'Add code to find things and copy
'We can use this line to copy from the Open Workbook, Sheet1, Range A2-A[Lastrow]
ActiveWorkbook.Sheets("Sheet1").Range("A2:A" & GetLastRow("A","Sheet1")).Copy _
ThisWb.Sheets("Sheet1").Range("A" & GetLastRow("A","Sheet1",ThisWb) + 1)
'to the bottom of our Master Sheet, column A
ActiveWorkbook.Close SaveChanges:=False
Next File
Application.ScreenUpdating = True
End Sub
Testing
I have the following files in a directory:
Tools1.xls: Tools2.xlsx":
When I run the macro on my "Master":
I am left with these results:
Edit:
If you want to add a function to your code, treat it as a separate subroutine.
For example:
Sub DoThings()
For x = 1 to 10
MsgBox(getLastRow("Sheet1",x))
Next x
End Sub
Function getLastRow(sheet As String, Col As Variant) As Integer
getLastRow = Sheets(sheet).Cells(Sheets(sheet).Rows.Count, Col).End(xlUp).Row
End Function

Related

Find and replace specific string inside a formula in multiple excel workbooks

I have a directory with 6 sub-folders and ~300 excel workbooks(Growing every day).
Each workbook has multiple formulas (~1200 per workbook) that reference a CSV data dump stored on a server path.
My issue is that excel treats the CSV data dump as "dirty data" and prompts warnings every time a workbook is opened claiming it can't update the links(But when the links are checked, excel then says there's no issue).
In all my research I've found there doesn't seem to be a way to fix this other than replace the datasource with a .xsl file which excel doesn't have any issues referencing.
What I need to do, is perform a find and replace on ~300 workbooks, find the CSV server path inside the formulas and replace it with the new server path for the .xls file.
I've tried "Sobolsoft's Excel Find and Replace" software, but that doesn't seem to want to look inside formulas to replace. I've used "Easy-XL" and "Kutools" both of which only work on open workbooks (Which I could live with, if I had to open 20-50 workbooks at a time, run the find and replace, then open the next batch) but neither of them wanted to work either.
I've used the following macro to unprotect/protect each workbook in the directory which works perfectly
Const cStartFolder = "M:\Transfer\DrillHole_Interaction\4.For_Survey" 'no slash at end
Const cFileFilter = "*.xlsm"
Const cPassword = "" 'use empty quotes if blank
Sub UnprotectAllWorksheets()
Dim i As Long, j As Long, arr() As String, wkb As Workbook, wks As Worksheet
ExtractFolder cStartFolder, arr()
On Error Resume Next
j = -1: j = UBound(arr)
On Error GoTo 0
For i = 0 To j
Set wkb = Workbooks.Open(arr(i), False)
For Each wks In wkb.Worksheets
wks.Protect cPassword, True, True
Next
wkb.Save
wkb.Close
Next
End Sub
Sub ExtractFolder(Folder As String, arr() As String)
Dim i As Long, objFS As Object, objFolder As Object, obj As Object
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFS.GetFolder(Folder)
For Each obj In objFolder.SubFolders
ExtractFolder obj.Path, arr()
Next
For Each obj In objFolder.Files
If obj.Name Like cFileFilter Then
On Error Resume Next
i = 0: i = UBound(arr) + 1
On Error GoTo 0
ReDim Preserve arr(i)
arr(i) = objFolder.Path & Application.PathSeparator & obj.Name
End If
Next
End Sub
If it would help, I'm also open to copying from a 'Master' workbook and copying the specific range into each other workbook (Copy range to range for each book) but I'm at my wits end and do not know how to proceed.
Any help would be appreciated.
No need to find and replace the csv fullname (path & filename) within all formulas, just change the links source at once within each workbook.
Try this within a loop through all workbooks that need to be changed.
Dim Wbk As Workbook
Application.DisplayAlerts = False
Set Wbk = Workbooks.Open(Filename:="WbkTarget.Fullname", UpdateLinks:=3)
With Wbk
.ChangeLink _
Name:="CsvFile.Fullname", _
NewName:="XlsFile.Fullname", _
Type:=xlExcelLinks
.Save
.Close
End With
Application.DisplayAlerts = True
where:
WbkTarget.Fullname: Path and name of the workbook with the link to be replaced
CsvFile.Fullname: Path and name of the csv file to be replaced
XlsFile.Fullname: Path and name of the xls that replaces the csv file

Excel VBA: How to Loop Through Workbooks in Same Folder using Given Code?

(Previous Post)
I need to create a macro that loops through the files that are in a single folder and runs the code that I have provided below. All the files are structured the same way however, have different data. The code helps me go to a specified destination file and counts the number of "YES" in the column. Then it outputs it into a CountResults.xlsm (master workbook). I have the following code with the help of Zac:
Private Sub CommandButton1_Click()
Dim oWBWithColumn As Workbook: Set oWBWithColumn = Application.Workbooks.Open("C:\Users\khanr1\Desktop\CodeUpdateTest\Test01.xlsx")
Dim oWS As Worksheet: Set oWS = oWBWithColumn.Worksheets("Sheet2")
ThisWorkbook.Worksheets("Sheet1").Range("B2").Value = Application.WorksheetFunction.CountIf(oWS.Range("B:B"), "YES")
oWBWithColumn.Close False
Set oWS = Nothing
Set oWBWithColumn = Nothing
End Sub
This is what the CountResults.xlsm (Master Workbook) looks like:
And, this is an example of what the Test01.xlsx looks like:
To note, there are 10 test files (Test01, Test02...) but the code should be able to update any new test files added (ex. Test11, Test12...). I had an idea of incorporating the "Files" column in the first image to pull the file names and loop them.
The easiest way to do so would be to use the filesystemobject to loop through all the files in the folder and find the ones where filename is similar to the predetrmined mask( in your case "Test*.xslx"). Please note that it also goes through subfolders in the specified folder. If that is not required, omit the first for each loop:
Dim fso As Object 'FileSystemObject
Dim fldStart As Object 'Folder
Dim fld As Object 'Folder
Dim fl As Object 'File
Dim oWBWithColumn As Workbook
Dim oWbMaster as workbook
Dim oWsSource as worksheet
Dim oWsTarget as worksheet
Dim Mask As String
Dim k as long
k=2
Set oWbMaster = ActiveWorkbook
Set oWsTarget = oWbMaster.Sheets("Sheet1")
Set fso = CreateObject("scripting.FileSystemObject")
Set fldStart = fso.GetFolder("C:\Users\khanr1\Desktop\CodeUpdateTest\")
Mask = "Test*" & ".xlsx"
For Each fld In fldStart.Subfolders
For Each fl In fld.Files
If fl.Name Like Mask Then
Set oWBWithColumn = Application.Workbooks.Open(Filename:=fld.Path & "\" & fl.Name, ReadOnly:=True)
Set oWsSource = oWBWithColumn.Worksheets("Sheet2")
oWsTarget.Range("B"& k).Value = Application.WorksheetFunction.CountIf(oWsSource.Range("B:B"), "YES")
oWBWithColumn.Close SaveChanges:=False
k = k+1
End If
Next
Next
If this answer helps, please mark as accepted. Also note that your original code would replace the value of B2 cell in the master spreadsheet every iteration of the loop, that's why I have added the k variable to change the target cell after each iteration
P.S.
You can generate a list of files along with the yes counts from the folder all at the same time, just add this line to the code before closing the file:
oWsTarget.Range("A"& k).Value= fl.Name
The easiest thing to do is convert your code into a function.
Private Sub CommandButton1_Click()
Dim r As Range
With Worksheets("Sheet1")
For Each r In .Range("A2", .Range("A" & .Rows.Count).End(xlUp))
r.Offset(0, 1).Value = getYesCount(r.Value)
Next
End With
End Sub
Function getYesCount(WorkBookName As String) As Long
Const FolderPath As String = "C:\Users\khanr1\Desktop\CodeUpdateTest\"
If Len(Dir(FolderPath & WorkBookName)) Then
With Workbooks.Open(FolderPath & WorkBookName)
With .Worksheets("Sheet2")
getYesCount = Application.CountIf(.Range("B:B"), "YES")
End With
.Close False
End With
Else
Debug.Print FolderPath & WorkBookName; ": Not Found"
End If
End Function

Import File Names to Excel Cells

I'm trying to implement a command button in my excel sheet that will prompt the user to select the files he wants to load before loading the selected files into the specified cells. So far I have tried using the following code but to no avail. I have been getting a type 13 error
Sub check11()
Dim FileName() As Variant
Dim f As String
Dim i As Variant
Dim j As Variant
Dim rng As Variant
rng = ActiveCell.Address
f = Application.GetOpenFilename("TXT File (*.txt), *.txt")
For i = 0 To 1
FileName(f) = j
Range(rng).Value = j
ActiveCell.Offset(1, 0).Select
Next i
End Sub
GetOpenFileName doesn't work in the way you have coded it. You can't select multiple files (as far as I'm aware) so the loop you run is redundant. The function will return either False if the user hits Cancel or the name of the file as a string. You ought to test for the Cancel case within your code.
I can't see the purpose of your loop or why you're selecting the offset cell. I think it might be a development error so I've just disregarded it.
Below is some code that shows you how the GetOpenFileName function could work in your context:
Sub check11()
Const TARGET_COL As String = "A" 'adjust letter for your column
Dim ws As Worksheet
Dim selectedFile As Variant
Dim cell As Range
'Find the next blank cell in target column
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set cell = ws.Cells(ws.Rows.Count, TARGET_COL).End(xlUp).Offset(1)
'Open the file dialog window
selectedFile = Application.GetOpenFilename("Text Files (*.txt), *.txt")
'Check if user hit cancel
If selectedFile = False Then Exit Sub
'Write the file name
cell.Value = selectedFile
End Sub
You have inccorect acces to Application.GetOpenFilename first of all you need to set MultiSelect to true so you will be able to select multiple files. Then this function will return array which you can loop. In loop i spliting filepath by "\" and with knowledge that filename is always last, i just write filename into cell.
Sub check11()
Dim filePath() As Variant
Dim i As Long
filePath = Application.GetOpenFilename("TXT File (*.txt), *.txt", MultiSelect:=True)
For i = 1 To UBound(filePath)
Sheets("Sheet2").Cells(1 + i, 1).Value = Split(filePath(i), "\")(UBound(Split(filePath(i), "\")))
Next i
End Sub

Trying to iterate through some workbooks from a list of workbooks, getting out of range errors

I have a problem. I'm guessing its easier to first write the code, and then explain it so here goes:
Sub Test()
Dim myHeadings() As String
Dim i As Long
Dim path As String
Dim pathtwo As String
Dim currentWb As Workbook
Dim openWb As Workbook
Dim openWs As Worksheet
Set currentWb = ActiveWorkbook
path = "C:\pathto\"
pfile = Split("File1,File2,File3", ",")
myHeadings = Split("Januari,Februari,Mars,April,Maj,Juni,Juli,Augusti,September,Oktober,November,December", ",")
For j = 0 To UBound(pfile)
pathtwo = path & pfile(j) & ".xlsx"
i = 0
If IsFile(pathtwo) = True Then
For i = 0 To UBound(myHeadings)
Set openWb = Workbooks.Open(pathtwo)
Set openWs = openWb.Sheets(myHeadings(i))
If openWs.Range("C34") = 0 Then
currentWb.Sheets("Indata").Cells(70, i + 27 + 12*j.Value = ""
Else
currentWb.Sheets("Indata").Cells(70, i + 27 + 12*j).Value = openWs.Range("C34")
End If
Next i
End if
Workbooks(openWb.Name).Close
Next j
End sub
What I want to pick a file from the pfile list, iterate through all its sheets defined in myHeadings and deduct the value at C34 (in reality there are plenty more values deducted, but to keep it short). After this I want to Close the file, go to the next file and do the same thing until all the Three files (again, in reality there are more, which some of them does not exist yet).
The function "IsFile" is
Function IsFile(fName As String) As Boolean
'Returns TRUE if the provided name points to an existing file.
'Returns FALSE if not existing, or if it's a folder
On Error Resume Next
IsFile = ((GetAttr(fName) And vbDirectory) <> vbDirectory)
End Function
written by iDevlop at stackoverflow, in this thread: VBA check if file exists
The reason why I have
currentWb.Sheets("Indata").Cells(70, i + 27 + 12*j).Value = openWs.Range("C34")
is because I want to start to write my data into currentWb at AA70 (Row 70, column 27). j*12 is because it is "periodic" depending on which file it is (the file file1 corresponds to 2015, file2 to 2016 etc), and hence in my summary I have it month and yearwise.
The problem arises though when I run this macro, at the first file at the sheet Mars I get out of range, but Before I added the iteration of files, there was not any subscript out of range at the first file. Is there anyone who can see how this can be?
Please note that indentation and so on may be somewhat off as I copied this from a much larger file with many rows in between with irrelevant code.
This isnt the right answer for your specific question but this is how I have done something similar and might help you to see how i did it. Basically what this is doing is opening up a CSV and copying the entire sheet and pasting it into a workbook. I was consolidating like 20 CSV dumps into 1 workbook to make it easier to dig through the stuff.
Regarding Dir()
You can invoke Dir with 2 arguments or with no arguments. You initialize it with 2 arguments the pathway and the attributes (which is optional). The 2nd time I am calling Dir in this sub it is without any arguments. What this does is fetch the subsequent files.
Sub Add_Sheets()
Dim ws As Worksheet
Dim PasteSheet As Worksheet
Dim wb As Workbook
Set wb = Application.Workbooks.Open("C:\Users\Desktop\CSV\All.xlsx") 'Location of where you want the workbook to be
StrFile = Dir("c:\Users\Desktop\CSV\*.csv") 'Dir of where all the CSVs were.
Do While Len(StrFile) > 0
Debug.Print StrFile
Application.Workbooks.Open ("c:\Users\Desktop\CSV\" & StrFile)
Set ws = ActiveSheet
ws.Range("A1:C" & rows.Count).Select 'Selecting Specific content on the worksheet
Selection.Copy
wb.Activate
wb.Worksheets.add(After:=Worksheets(Worksheets.Count)).name = StrFile 'Setting the sheet name to the name of the CSV file
Range("A1").PasteSpecial Paste:=xlPasteValues
StrFile = Dir
Loop
End Sub

Run macro on all files open in taskbar one by one

My work is regarding formating 100 of files everyday. though i have a macro desined for the purpose but i have to run the macro on each and every file one after saving previous.
my question is how can i be able to run my macro on these opened workbooks in one step. As i save one it would run on other one in the queue.
Put the following macro in a "BASE" workbook as Passerby mentioned
Sub SO()
Dim macroList As Object
Dim workbookName As String
Dim wbFullPath
Dim macroName As String
Dim currentWb As Workbook
Dim masterWb As Workbook ' the Excel file you are calling this procedure from
Dim useWbList As Boolean
Dim height As Long, i As Long
Dim dataArray As Variant
useWbList = False ' DEFINE which input method
Set macroList = CreateObject("Scripting.Dictionary")
If useWbList Then
' you can also from the dictionary from 2 columns of an excel file , probably better for management
With masterWb.Worksheets("Sheet1") '<~~ change Sheet1 to the sheet name storing the data
height = .Cells(.Rows.Count, 1).End(xlUp).Row ' Assume data in column A,B, starting from row 1
If height > 1 Then
ReDim dataArray(1 To height, 1 To 2)
dataArray = .Range(.Cells(1, 1), .Cells(height, 2)).Value
For i = 1 To height
macroList.Add dataArray(i, 1), dataArray(i, 2)
Next i
Else
'height = 1 case
macroList.Add .Cells(1, 1).Value, .Cells(1, 2).Value
End If
End With
Else
' ENTER THE FULl PATH in 1st agrument below, Macro Name in 2nd argument
' Remember to make sure the macro is PUBLIC, try to put them in Module inside of Sheets'
macroList.Add "C:\Users\wangCL\Desktop\Book1.xlsm", "ThisWorkbook.testing"
'macroList.Add "FULL PATH", "MACRO NAME"
'macroList.Add "FULL PATH", "MACRO NAME"
'macroList.Add "FULL PATH", "MACRO NAME"
End If
Application.DisplayAlerts = False
For Each wbFullPath In macroList.keys
On Error GoTo 0
macroName = macroList.Item(workbookName)
workbookName = Mid(wbFullPath, InStrRev(wbFullPath, "\") + 1)
Err.Clear
On Error Resume Next
Set currentWb = Nothing
Set currentWb = Workbooks(workbookName) ' see if the workbook is already open
If Err.Number <> 0 Then
' open the workbook if workbook NOT opened
Set currentWb = Workbooks.Open(workbookName, ReadOnly:=True)
End If
On Error GoTo 0
' run the macro
Application.Run workbookName & "!" & macroList.Item(wbFullPath)
'close the workbook after running the macro
currentWb.Close saveChanges:=False
Set currentWb = Nothing
Next wbFullPath
End Sub
Hope it helps and please let me know if there's anything unclear
I have got my solve using below code.
Sub OpenAllWorkbooksnew()
Set destWB = ActiveWorkbook
Dim DestCell As Range
Dim cwb As Workbook
For Each cwb In Workbooks
**Call donemovementReport**
ActiveWorkbook.Close True
ActiveWorkbook.Close False
Next cwb
End Sub