How do I copy a range from one workbook to another in excel WITHOUT having to name it in VBA? - vba

I'm looking for assistance regarding how to write a VBA command that allows me to copy a range of cells from different workbooks onto one master sheet. Let me explain further.
Everyday I receive a new excel document from my clients named based on the date it was uploaded ie. September 18, 2018 file would be called A20180918.
Once i've received a week's worth of excel files (A20180918-A20180921), I then have to copy certain information from the original uploaded file to a master tracking sheet.
So, my hurdle is such that each time I record my actions into a macro, the code includes the file name which then creates a subscript error when it's run on the next day's file.
So here's an example below of the code I have this far:
Sub CopyRange()
CopyRange Macro
'This is the line of the code that's causing problems given it's a specified workbook name
'and when I try to open tomorrow's workbook i'll run into the subscript error.
Windows("A20180914.xls").Activate
Range("A2:B2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Windows("Master Sheet.xlsm").Activate
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
Thank you!

Here's two solutions. One to scan an Directory for files, and the other to select files. I see they've both been suggested in the comments already. :p
Sub Test()
' If you want to scan an "unprocessed files" directory
'Call ScanDir("C:\Test\")
' If you want to select files to process
Call SelectFiles
End Sub
Private Sub ScanDir(ByVal DirPath As String)
Dim oCurFile As String
oCurFile = Dir(DirPath)
Do While oCurFile <> ""
' Add the work to the file here
' Filename: DirPath & oCurFile
oCurFile = Dir()
Loop
End Sub
Private Sub SelectFiles()
Dim oFileDialog As FileDialog
Set oFileDialog = Application.FileDialog(msoFileDialogFilePicker)
oFileDialog.AllowMultiSelect = True
If oFileDialog.Show = -1 Then
Dim oFile As Variant
For Each oFile In oFileDialog.SelectedItems
' Add the work to the file here
' Filename: oFile
Next
End If
End Sub

By the looks of it you have all the workbooks open when you run the code - there are ways to have the code open each workbook in a certain folder, or ask the user to select them.
While writing this other answers have given the code for selecting files from folders.
Each workbook in the Excel Application is held in a collection of workbooks. The good thing about collections is you can step through them without know the specifics before you get there.
The code below will print the name of each workbook you have open into the immediate window. Note - these are in the same instance of Excel. If you open Excel a second time then any workbooks in that application will be in a different collection.
You don't really need the Application but I left it in to make things a bit clearer.
Sub Test()
Dim bk As Workbook
For Each bk In Application.Workbooks
Debug.Print bk.Name
Next bk
End Sub
This is the full code - note that nothing is Selected.
Sub Test()
Dim bk As Workbook
Dim Master As Workbook
Dim LastCell As Range
Set Master = Workbooks("Master Sheet.xlsm")
For Each bk In Application.Workbooks
'Checks the middle part of the file name - should be a number.
'Better ways to check the file name are available.
If IsNumeric(Mid(bk.Name, 3, 8)) Then
'Copy date from Sheet1. It's assumed each row in
'column B is populated and figures out the last cell from there.
With bk.Worksheets("Sheet1")
Set LastCell = .Cells(.Rows.Count, 2).End(xlUp)
.Range("A1", LastCell).Copy
End With
'Pastes the results to Sheet1 in the Master workbook.
'The last cell containing data in column A is found and
'then offset by 1 row.
With Master.Worksheets("Sheet1")
.Range("A" & .Rows.Count).End(xlUp).Offset(1).PasteSpecial Paste:=xlPasteValues
End With
End If
Next bk
End Sub

This will do it, you just need to supply the sheet name and ranges where noted:
Sub copyRange()
'File system variables
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim myDir As String
Dim Mask As String
'Workbook variables
Dim wb As Workbook
Dim sh As Worksheet
myDir = "C:\Users\Guest\Desktop" 'Insert the path where your incoming files are stored.
Mask = "*.xl??" 'This makes it so it only looks at Excel files.
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.getFolder(myDir)
For Each objFile In objFolder.Files
If LCase(objFile.Name) Like LCase(Mask) Then
Set wb = Workbooks.Open(myDir & "\" & objFile.Name, , True) 'This is set to open in read only, to avoid issues with the file already being open
'The ranges you are copying/pasting are vague, supply the sheet names and ranges below
'Get Copy range with dynamic number of rows.
With wb.Sheets("Sheet1").Range("A2:B2") '<---- Specify Sheet/Range
Set copyRange = .Resize(.End(xlDown).Row, 2)
End With
'Get next available row in paste range.
With ThisWorkbook.Sheets("Sheet1").Range("G:H") '<---- Specify Sheet/Range
Set pasteRange = .Cells(.Rows.Count, 1).End(xlUp).Offset(1, 0)
End With
copyRange.Copy pasteRange
wb.Close False
End If
Next objFile
End Sub

Related

VBA to paste data into existing workbook without specifying workbook name?

I am creating a workbook which will be used as a template for monthly reports (let's call it 'ReportWorkbookTest') and am struggling to write or record a macro which will paste data into the ReportWorkbookTest from various, unspecified workbooks.
To create the monthly reports, data is exported from a server to a .xlsx file named by the date/time the report was exported. Therefore, the name of the workbook which information will be pasted form will always have different names. The columns that the information in the monthly data exports will always remain the same (columns D:G & I). I've managed to do this for two specified workbooks but cannot transpose to new monthly data exports.
Range("I4").Select
Windows("Export 2018-06-21 11.51.34.xlsx").Activate
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=9, Criteria1:= _
xlFilterLastMonth, Operator:=xlFilterDynamic
Range("D2:G830,I2:I830").Select
Range("I2").Activate
Selection.Copy
Windows("ReportWorkbookTest.xlsm").Activate
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Is there a way to set up the VBA so that the workbook names do not need to be specified while running the macro? Also, how do I specify that the macro only copies the active rows in the table if the number of rows changes per export?
Thanks!
If only these two workbooks will be open you can use numbers instead of the name:
Workbooks(1)
and
Workbooks(2)
Workbooks(1) will be the one that was opened first, more likely ReportWorkbookTest.xlsm where the macro will be, so you can provide instructions that this file should be opened first. If more than these two workbooks will be open you can try a loop approach, here is an example to use:
Dim wkb as Workbook
Dim thisWb as Workbook
Dim expWb as Workbook
Set thisWb = ThisWorkbook
For Each wkb in Workbooks
If wkb.Name Like "Export 2018-*" Then
expWb = wkb
Exit For
End If
Next
If Not expWb Is Nothing Then
'Found Export, do stuff like copy from expWb to thisWb
expWb.Worksheets(1).Range("B20:B40").Copy
thisWb.Sheets("PasteSheet").Range("A3").PasteSpecial xlValues
Else
'Workbook with Export name not found
End If
This is your framework, if you have multiple files to import then I would suggest a wizard instead.
Wizard framework would be:
1) prompt the user to select a file (of a certain type you might check for, can be a column name - header)
2) if it passes validation then import the data (and process it)
2b) if doesn't pass report it wasn't a valid file and prompt again
3) prompt for the next file type
......
I have a project like this that takes 4 different data "dumps" and merges them into a summary workbook each month.
But for a single file of changing name, here you go for a framework:
you can eliminate cycling through all of the worksheets if there is only one
you might also not be appending data to what already exists, but that is what finding the new last row is for.
Option Explicit
'Sub to get the Current FileName
Private Sub getFN()
Dim Finfo As String
Dim FilterIndex As Long
Dim Title As String
Dim CopyBook As Workbook 'Workbook to copy from
Dim CopySheet As Worksheet 'Worksheet to copy from
Dim FN As Variant 'File Name
Dim wsNum As Double 'worksheet # as you move through the Copy Book
Dim cwsLastRow As Long 'copy worksheet last row
Dim mwsLastRow As Long 'master worksheet last row
Dim masterWS As Worksheet 'thisworkbook, your master worksheet
Dim rngCopy1 As Range
Dim rngCopy2 As Range
Set masterWS = ThisWorkbook.Worksheets("Master Security Logs")
'Set up file filter
Finfo = "Excel Files (*.xls*),*.xls*"
'Set filter index to Excel Files by default in case more are added
FilterIndex = 1
' set Caption for dialogue box
Title = "Select the Current AP Reconcile Workbook"
'get the Forecast Filename
FN = Application.GetOpenFilename(Finfo, FilterIndex, Title)
'Handle file Selection
If FN = False Then
MsgBox "No file was selected.", vbExclamation, "Not so fast"
Else
'Do your Macro tasks here
'Supress Screen Updating but don't so this until you know your code runs well
Application.ScreenUpdating = False
'Open the File
Workbooks.Open (FN)
'Hide the file so it is out of the way
Set CopyBook = ActiveWorkbook
For wsNum = 1 To CopyBook.Sheets.Count 'you stated there will be 8, this is safer
'Do your work here, looks like you are copying certain ranges from each sheet into ThisWorkbook
CopySheet = CopyBook.Worksheets(wsNum) '1,2,3,4,5,6,7,8
'Finds the lastRow in your Copysheet each time through
cwsLastRow = CopySheet.Cells(CopySheet.Rows.Count, "A").End(xlUp).Row
'Set your copy ranges
Set rngCopy1 = CopySheet("D2:D"&cwsLastRow) 'this is your D column
Set rngCopy2 = CopySheet("I2:I"&cwsLastRow) 'this is your I column
'so you would have to keep tabs on what the lastRow of this sheet is too and always start at +1
mwsLastRow = masterWS.Cells(masterWS.Rows.Count, "A").End(xlUp).Row
'Copy the ranges in where you want them on the master sheet
'rngCopy1.Copy destination:= masterWS.Range("D"&mwsLastRow+1)
'rngCopy2.Copy destination:= masterWS.Range("I"&mwsLastRow+1)
'Clear the clipboard before you go around again
Application.CutCopyMode = False
Next wsNum
End If
'Close the workbook opened for the copy
CopyBook.Close savechanges:=False 'Not needed now
'Screen Updating Back on
Application.ScreenUpdating = True
End Sub

Excel VBA Copy Range Transpose from Another Spreadsheet

I want to copy a range from a workbook and transpose it into my current sheet.
Why would I get a "Subscript out of range" error on this line:
Workbooks("Libraries\Documents\Book1.xlsx").Worksheets("Sheet1").Range("A1:A5").Copy
Sub PasteSpecial_Examples()
'https://stackoverflow.com/questions/8852717/excel-vba-range-copy-transpose-paste
'https://www.excelcampus.com/vba/copy-paste-cells-vba-macros/
Workbooks("Libraries\Documents\Book1.xlsx").Worksheets("Sheet1").Range("A1:A5").Copy
ActiveSheet.Range("A1").PasteSpecial Transpose:=True
End Sub
Excel only permits one workbook open with a certain filename at the same time, even if those workbooks exist in different directories (which they must, or they couldn't have the same filename).
The Workbooks collection's index is just the filename, not the fully-qualified path and name.
I'm not sure whether the first point is the reason for the second point, or whether the second point is the reason for the first point, but they will be related.
So your code should be:
Sub PasteSpecial_Examples()
Workbooks("Book1.xlsx").Worksheets("Sheet1").Range("A1:A5").Copy
ActiveSheet.Range("A1").PasteSpecial Transpose:=True
End Sub
Based on comments implying that you haven't yet opened Libraries\Documents\Book1.xlsx when you run your code, you could do this:
Sub PasteSpecial_Examples()
Dim wsDst As WorkSheet
Set wsDst = ActiveSheet
Workbooks.Open "Libraries\Documents\Book1.xlsx"
Workbooks("Book1.xlsx").Worksheets("Sheet1").Range("A1:A5").Copy
wsDst.Range("A1").PasteSpecial Transpose:=True
End Sub
which continues to refer to the workbook by its name.
Or, slightly better, do this:
Sub PasteSpecial_Examples()
Dim wbSrc As WorkBook
Dim wsDst As WorkSheet
Set wsDst = ActiveSheet
Set wbSrc = Workbooks.Open("Libraries\Documents\Book1.xlsx")
wbSrc.Worksheets("Sheet1").Range("A1:A5").Copy
wsDst.Range("A1").PasteSpecial Transpose:=True
End Sub
which assigns a Workbook object to refer to the newly opened workbook and then uses that object in the Copy statement.
Note: In this code "Libraries\Documents\Book1.xlsx" is a relative reference to the file, e.g. if the current directory was C:\Temp then it would look for the file C:\Temp\Libraries\Documents\Book1.xlsx. You should seriously consider using an absolute reference if possible.
I do it like this:
Dim Finfo As String
Dim FilterIndex As Long
Dim Title As String
Dim ExportFilename As Variant
Dim CopyBook As Workbook
Dim CopySheet As Worksheet
Dim MnthName As String
'Set up file filter
Finfo = "Excel Files (*.xls*),*.xls*"
'Set filter index to Excel Files by default in case more are added
FilterIndex = 1
' set Caption for dialogue box
Title = "Select a the DD Revenue Master file to Export to"
'get the Forecast Filename
ExportFilename = Application.GetOpenFilename(Finfo, FilterIndex, Title)
'Handle file Selection
If ExportFilename = False Then
'No Export File was Selected
MsgBox "No file was selected"
Else
'Check and see if this is a correct Export File
Workbooks.Open (ExportFilename)
Set CopyBook = ActiveWorkbook
Set CopySheet = CopyBook.Worksheets(1)
MsgBox "Valid File Selected."
Application.CutCopyMode = False
revenueSheet.Range("A1:BO500").Copy
CopyBook.Worksheets(1).Activate
CopyBook.Worksheets(1).Range("A1").PasteSpecial Paste:=xlPasteColumnWidths
CopyBook.Worksheets(1).Range("A1").PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False 'erase the clipboard
'close your stuff that you dont want open
End If
End Sub
Don't forget to close your workbooks when you are done. I had to trim a bunch of code because my file launches into a large case select. But often you select a workbook, open it, select some data, copy it, and paste it, close the workbook. Happens alot. Hope this helps. I believe that I found that you had to activate the newly selected workbook to perform actions on it. You can always refer to the workbook with the code in it as ThisWorkbook
To avoid confusion and since they are used in a bunch of modules I have a global variables module with the following in it but you could do this at the top of the sub if you don't have a complex project.
Option Explicit
Public thisWB As Workbook
Public functionSheet As Worksheet
Public revenueSheet As Worksheet
Public salesSheet As Worksheet
Public scratchSheet As Worksheet
Public lastRow As Double
'**********************************************************
'This sub routine will be used to intialize public variables
'**********************************************************
Private Sub SetPublicVariables()
Set thisWB = ActiveWorkbook
Set functionSheet = thisWB.Worksheets("Data Functions")
Set revenueSheet = thisWB.Worksheets("DD Monthly Revenue")
Set salesSheet = thisWB.Worksheets("Salespersons")
Set scratchSheet = thisWB.Worksheets("ScratchSheet")
End Sub
I use this method alot . . . . . .
Oh, I call the public variable set up upon workbook open (you can find that method). In order to call a private sub you must use.
Application.Run "Global_Variables.SetPublicVariables"
'that is modulename.methodname if you want to pass arguments following
'Application.Run "modulename.methodname", arg1, arg2, etc.
Cheers, Happy coding - WWC

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 - Open Workbooks given names

I have the below code.
Very simply it asks the user to select multiple excel workbooks and then will copy and paste data from those workbooks to the current work book.
1.
I would like to add the functionality, whereby instead of the user selecting the excel workbooks. The excel workbooks will be selected in that their names are listed on the current excel sheet.
For example - Select excel workbooks in specified folder whose names are listed in A1:A5.
I would like to perform automatic processing on the data before it is copied into the current work book.
For example if workbook name = 100.xlsx then multiply selection by 15.
See my current code
Sub SUM_BalanceSheet()
Application.ScreenUpdating = False
'FileNames is array of file names, file is for loop, wb is for the open file within loop
'PasteSheet is the sheet where we'll paste all this information
'lastCol will find the last column of PasteSheet, where we want to paste our values
Dim FileNames
Dim file
Dim wb As Workbook
Dim PasteSheet As Worksheet
Dim lastCol As Long
Set PasteSheet = ActiveSheet
lastCol = PasteSheet.Cells(1, Columns.Count).End(xlToLeft).Column
'Build the array of FileNames to pull data from
FileNames = Application.GetOpenFilename(filefilter:="Excel Files (*.xlsx), *.xlsx", MultiSelect:=True)
'If user clicks cancel, exit sub rather than throw an error
If Not IsArray(FileNames) Then Exit Sub
'Loop through selected files, put file name in row 1, paste P18:P22 as values
'below each file's filename. Paste in successive columns
For Each file In FileNames
Set wb = Workbooks.Open(file, UpdateLinks:=0)
PasteSheet.Cells(1, lastCol + 1) = wb.Name
wb.Sheets("Page 1").Range("L14:L98").Copy
PasteSheet.Cells(2, lastCol + 1).PasteSpecial Paste:=xlPasteValues
wb.Close SaveChanges:=False
lastCol = lastCol + 1
Next
'If it was a blank sheet then data will start pasting in column B, and we don't
'want a blank column A, so delete it if it's blank
If Cells(1, 1) = "" Then Cells(1, 1).EntireColumn.Delete shift:=xlLeft
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
This is a frame that needs fine-tuning, but you can get the idea:
Dim i&, wbName$
Dim rng As Excel.Range
Dim wb, wb1 As Excel.Workbook
Set wb = Application.ThisWorkbook
Set rng = wb.Sheets("Sheet1").Range("A1")
For i = 0 To 14
wbName = CStr(rng.Offset(i, 0).Value)
On Error Resume Next 'Disable error handling. We will check whether wb is nothing later
wb1 = Application.Workbooks.Open(wbName, False)
On Error GoTo ErrorHandler
If Not IsNothing(wb1) Then
'Copy-paste here
If wb1.Name = "100" Then 'any condition(s)
'Multiply, divide, or whatever
End If
End If
Next
ErrorHandler:
MsgBox "Error " & Err.Description
'Add additional error handling
Try not to use ActiveSheet and ActiveWorkbook without absolute need. Use ThisWorkbook, dedicated Workbook object, and named sheet Workbook.Sheets("Name") or Workbook.Sheets(index) instead.
Alternatively instead of disabling error checking you can do it and fail if a file is missing.

Excel loop macro ending early and needing to keep files open to copy several loops(different files)

I'm having a bit of a problem with this VBA code
Sub upONGOING_Train1()
ScreenUpdating = False
'set variables
Dim rFndCell As Range
Dim strData As String
Dim stFnd As String
Dim fCol As Integer
Dim oCol As Integer
Dim SH As Worksheet
Dim WS As Worksheet
Dim strFName As String
Dim objCell As Object
Set WS = ThisWorkbook.Sheets("Trains")
For Each objCell In WS.Range("L3:L100")
oCol = objCell.Column
strFName = WS.Cells(, oCol).Offset(objCell.Row - 1, 0)
On Error GoTo BLANK: 'skip macro if no train
Workbooks.Open Filename:=strFName 'open ongoing report
Set SH = Worksheets("Trains") 'set sheet
stFnd = WS.Cells(, oCol).Offset(objCell.Row - 1, 2).Value 'set connote
With SH
Set rFndCell = .Range("C3:C1100").Find(stFnd, LookIn:=xlValues)
If Not rFndCell Is Nothing Then
fCol = rFndCell.Column
WS.Cells(, oCol).Offset(objCell.Row - 1, 3).Resize(1, 6).Copy
SH.Cells(, fCol).Offset(rFndCell.Row - 1, 10).Resize(1, 6).PasteSpecial xlPasteValues 'paste values in ongoing report if connote found
ActiveWorkbook.Save 'save ongoing report
ActiveWorkbook.Close 'close ongoing report
Else 'Can't find the item
End If
End With
BLANK:
Next objCell
ScreenUpdating = True
End Sub
What I want it to do is - for every row in L3:L100
Open file listed in column "L" (if there or skip line to next one) and go to sheet
Match value from original sheet column "N" to "C3:C1100" in newly opened sheet
Copy columns "O:T" and paste relative to the matching value in the opened sheet(M:R) and save
However when I leave a gap of 2 rows it gives me the error for file not found instead of proceeding to the next loop like it does when there is only 1 row missing.
Seems i can't post images yet.
Also if anyone can point me in a good direction on how to open the sheet in the cell reference only if it is not already open it will usually only have 2 files to use (max of 4 at end of quarter).
Its just too much trouble to click OK on all the windows that pop up when you try to reopen an already open workbook.
If its any help to get your head around it.
I have 2 separate reports for 2 clients(new each quarter so max of 4 sheets at a time) that will already have the names to be searched (2 sheets in each book).
Any help would be greatly appreciated
Thanks heaps
Thanks to those who have put forth suggestions and code.
I'll them out tomorrow and failing that I've just come up with another idea that to re-purpose some other code I have but didn't realize would help.
The code basically copies what I need to a blank tab and deletes rows with a given value - with some formulas to help sort this would give me a block of rows with no breaks all going to the same destination file.
Thus allowing me to run the (a bit more streamlined Thanks everyone) loop over the remaining rows.
On Error GoTo BLANK
Workbooks.Open Filename:=strFName
Change the above into this:
On Error Resume Next
Workbooks.Open Filename:=strFName
If Err.Number <> 0 Then Goto Blank
As to hpw keep the workbook open, you can leave it open (no .close) but then when you want to open it check first if it is open (i.e. using Workbooks("name")), with some error handling using the same mechanism as above, if error exists then the wb is not already open, you open it.
Finally, avoid counting on the Active stuff, such as the ActiveWorkbook`. Instead, make an explicit reference to you wb, i.e.:
Set wb = Workbooks.Open(Filename:=strFName)
Set SH = wb.Worksheets("Trains")
to consider only not blank cells you can use SpecialCells() method of Range object and leave off any On Error GoTo statements, that should be used in very a few limited cases (one of which we'll see in a second)
furthermore you're using some uselessly long winded 'loops' to reference your relevant cells, for instance:
WS.Cells(, oCol).Offset(objCell.Row - 1, 0)
is equivalent to objCell itself!
and there are some more examples of that kind
finally, let's come to the workbooks open/close issue
you could:
use a Dictionary object to store the name of all opened workbooks so as to leave then open throughout your macro and close them all by the end of it
adopt a helper function that tries to set the wanted sheet (i.e. "Trains") in the wanted workbook (i.e. the one whose name is the current objCell value) and return False if not successful
all what above in this refactoring of your code:
Sub upONGOING_Train1bis()
Dim rFndCell As Range
Dim SH As Worksheet
Dim objCell As Range
Dim shtDict As New Scripting.Dictionary '<--| this is the dictionary that will store every opened workbook name as its 'keys'
Dim key As Variant
' Dim dec As String '<--| do you actually need it?
Application.ScreenUpdating = False
With ThisWorkbook.Sheets("Trains") '<-- reference your working worksheet
' dec = .Range("L1") '<-- what's this for? in any case take it out of for loops since its value doesn't depend on current loop variables
For Each objCell In .Range("L3:L100").SpecialCells(xlCellTypeConstants) '<--| loop through L3:L100 range not blank cells only
If TrySetWorksheet(objCell.Value, "Trains", SH) Then '<--|Try to set the wanted worksheet in the wanted workbook: if successful it'd retrun 'True' and leave you with 'SH' variable set to the wanted worksheet
shtDict(SH.Parent.Name) = shtDict(SH.Parent.Name) + 1
Set rFndCell = SH.Range("C3:C1100").Find(objCell.Offset(, 2).Value, LookIn:=xlValues, lookAt:=xlWhole) '<--| specify at least 'LookIn' and 'LookAt' parameters
If Not rFndCell Is Nothing Then rFndCell.Offset(, 10).Resize(, 6).Value = objCell.Offset(, 3).Resize(, 6).Value
End If
Next objCell
End With
For Each key In shtDict.Keys '<--|loop through opened workbooks dictionary keys
Workbooks(key).Close True '<--| close workbook whose name corresponds to current dictionary key
Next
Application.ScreenUpdating = True
End Sub
Function TrySetWorksheet(fileName As String, shtname As String, sht As Worksheet) As Boolean
Set sht = Nothing
On Error Resume Next
Set sht = Workbooks(Right(fileName, Len(fileName) - InStrRev(fileName, "\"))).Worksheets(shtname) '<--| try looking for an already open workbook with wanted name and wanted sheet
If sht Is Nothing Then Set sht = Workbooks.Open(fileName:=fileName).Worksheets(shtname) '<--| if not found then try opening the wanted workbook and set the wanted sheet in it
TrySetWorksheet = Not sht Is Nothing '<--| set the return value to the final result of attempts at locating the wanted sheet
End Function