Copy same column from multiple worksheets to new worksheet - excel

I have a workbook with about 20 sheets that has about 130 rows. What I'd like to do is copy column B from each worksheet and paste into either a new worksheet or a new workbook. Either is fine, I've tried both ways and I can seem to get the column B data from each worksheet to be in separate columns.
I have tried the following code and it seems to loop through the sheets but it only retains column B from the last sheet.
Is there a way to modify this code to paste each column B from each worksheet in a new column in the new sheet? I've tried other code snippets from posts here and none seem to do the final task.
Sub CopyColumns()
Dim Source As Worksheet
Dim Destination As Worksheet
Dim Last As Long
Application.ScreenUpdating = False
For Each Source In ThisWorkbook.Worksheets
If Source.Name = "Master" Then
MsgBox "Master sheet already exist"
Exit Sub
End If
Next
Set Destination = Worksheets.Add(after:=Worksheets("summary"))
Destination.Name = "Master"
For Each Source In ThisWorkbook.Worksheets
If Source.Name <> "Master" And Source.Name <> "summary" Then
Last = Destination.Range("A1").SpecialCells(xlCellTypeLastCell).Column
If Last = 1 Then
Source.Range("B4:B129").Copy Destination.Columns(Last)
Else
Source.Range("B4:B129").Copy Destination.Columns(Last + 1)
End If
End If
Next Source
I have also tried the following to no avail
For Each ws In ActiveWorkbook.Worksheets
Set oldcol = ws.Range("B5:B129")
Set newcol = Workbooks("OctTotals.xlsm").Worksheets(1).Columns("B")
oldcol.Copy Destination:=newcol
oldcol.PasteSpecial xlPasteValues
WorksheetFunction.Transpose (newcol.Value)
Next ws
Any assistance would be appreciated!

Untested:
Sub CopyColumns()
Dim Source As Worksheet
Dim Destination As Worksheet
Dim rngDest As Range
Application.ScreenUpdating = False
For Each Source In ThisWorkbook.Worksheets
If Source.Name = "Master" Then
MsgBox "Master sheet already exist"
Exit Sub
End If
Next
Set Destination = Worksheets.Add(after:=Worksheets("summary"))
Destination.Name = "Master"
Set rngDest = Destination.Range("A1") '<< for example: first paste location
For Each Source In ThisWorkbook.Worksheets
If Source.Name <> "Master" And Source.Name <> "summary" Then
Source.Range("B4:B129").Copy rngDest
Set rngDest = rngDest.Offset(0, 1) '<< next column over
End If
Next Source
End Sub

Same Column From Multiple Worksheets to New Worksheet
Copy the complete code into a standard module (e.g. Module1).
Carefully adjust the values in the constants section of the Sub.
Only run the Sub. The Function is called by the Sub.
If you need to place the Target Worksheet before another worksheet,
change wb.Worksheets.Add , wb.Worksheets(AfterSheetNameOrIndex) to
wb.Worksheets.Add wb.Worksheets(AfterSheetNameOrIndex).
The Code
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Copies values of a specified column of each (with possible '
' exceptions) worksheet in a workbook to a newly created '
' worksheet in the same workbook. '
' Remarks: If the worksheet to be created already exists, it will be '
' deleted. Then the result will be calculated and only now '
' the worksheet will be newly created to "recieve the data". '
' The Exceptions Array can be empty (""), or can contain one '
' worksheet name or a comma-separated list of worksheet names. '
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub copyColumns()
On Error GoTo cleanError
Const Proc As String = "CopyColumns"
Const srcFirstRow As Long = 4
Const srcCol As Variant = 2
Const tgtName As String = "Master"
Const tgtFirstCell As String = "A1"
Const AfterSheetNameOrIndex As Variant = "Summary"
Dim Exceptions As Variant
Exceptions = Array("Summary")
' Define workbook.
Dim wb As Workbook: Set wb = ThisWorkbook
' Delete possibly existing Target Worksheet.
On Error Resume Next
Application.DisplayAlerts = False
wb.Worksheets(tgtName).Delete
Application.DisplayAlerts = True
On Error GoTo cleanError
' Write values from each Source Worksheet to Sources Array of Arrays.
Dim Sources As Variant: ReDim Sources(1 To wb.Worksheets.Count)
Dim ws As Worksheet, r As Long, c As Long
For Each ws In ThisWorkbook.Worksheets
If IsError(Application.Match(ws.Name, Exceptions, 0)) Then
c = c + 1
Sources(c) = getColumnValues(ws, srcCol, srcFirstRow)
If Not IsEmpty(Sources(c)) Then
If UBound(Sources(c)) > r Then r = UBound(Sources(c))
Debug.Print r, c, UBound(Sources(c)), "Not Empty"
Else
Debug.Print r, c, "Empty"
End If
End If
Next ws
ReDim Preserve Sources(1 To c)
' Write values from Source Array of Arrays to Target Array.
Dim Target As Variant: ReDim Target(1 To r, 1 To c)
Dim j As Long, i As Long
For j = 1 To c
If Not IsEmpty(Sources(j)) Then
For i = 1 To UBound(Sources(j))
Target(i, j) = Sources(j)(i, 1)
Next i
End If
Next j
' Write values from Target Array to Target Worksheet.
wb.Worksheets.Add , wb.Worksheets(AfterSheetNameOrIndex)
Set ws = wb.ActiveSheet
ws.Name = tgtName
ws.Range(tgtFirstCell).Resize(r, c) = Target
' Inform user.
MsgBox "Data copied.", vbInformation, "Success"
Exit Sub
cleanError:
MsgBox "An unexpected error occurred in '" & Proc & "'." & vbCr _
& "Run-time error '" & Err.Number & "':" & vbCr & Err.Description, _
vbCritical, Proc & " Error"
On Error GoTo 0
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Writes the values of a non-empty one-column range starting '
' from a specified row, to a 2D one-based one-column array. '
' Returns: A 2D one-based one-column array. '
' Remarks: If the column is empty or its last non-empty row is above '
' the specified row or if an error occurs the function will '
' return an empty variant. Therefore the function's result '
' can be tested with "IsEmpty". '
' If showMessages is set to true, a message box will be '
' displayed; so use it with caution. '
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function getColumnValues(Sheet As Worksheet, _
Optional ByVal AnyColumn As Variant = 1, _
Optional ByVal FirstRow As Long = 1, _
Optional ByVal showMessages As Boolean = False) _
As Variant
' Prepare.
Const Proc As String = "getColumnValues"
If showMessages Then
Dim msg As String
End If
On Error GoTo cleanError
' Define Column Range.
Dim rng As Range
Set rng = Sheet.Columns(AnyColumn).Find("*", , xlValues, , , xlPrevious)
If rng Is Nothing Then GoTo EmptyColumnIssue
If rng.Row < FirstRow Then GoTo FirstRowIssue
Set rng = Sheet.Range(Sheet.Cells(FirstRow, AnyColumn), rng)
' Write values from Column Range to Column Array.
Dim Result As Variant
If rng.Rows.Count = 1 Then
ReDim Result(1 To 1, 1 To 1): Result(1, 1) = rng.Value
Else
Result = rng.Value
End If
getColumnValues = Result
' Possibly inform user.
GoSub writeSuccess
Exit Function
writeSuccess:
If showMessages Then
If UBound(Result) > 1 Then msg = "s"
msg = "Range '" & rng.Address(0, 0) & "' " _
& "was successfully written to the 2D one-based " _
& "one-column array containing '" & UBound(Result) & "' " _
& "element" & msg & " (row" & msg & ")."
GoSub msgWSB
MsgBox msg, vbInformation, Proc & ": Success"
End If
Return
EmptyColumnIssue:
If showMessages Then
msg = "Column '" & AnyColumn & "' is empty."
GoSub msgWSB
MsgBox msg, vbExclamation, Proc & ": Empty Column Issue"
End If
Exit Function
FirstRowIssue:
If showMessages Then
msg = "The last non-empty row '" & rng.Row & "' " _
& "is smaller than the specified first row '" & FirstRow & "'."
GoSub msgWSB
MsgBox msg, vbExclamation, Proc & ": First Row Issue"
End If
Exit Function
msgWSB:
msg = msg & vbCr & vbCr & "Worksheet: '" & Sheet.Name & "'" & vbCr _
& "Workbook : '" & Sheet.Parent.Name & "'"
Return
cleanError:
If showMessages Then
MsgBox "An unexpected error occurred in '" & Proc & "'." & vbCr _
& "Run-time error '" & Err.Number & "':" & vbCr & Err.Description _
, vbCritical, Proc & " Error"
End If
On Error GoTo 0
End Function

Related

How to copy a range and paste diagonally using vba

Hi If I have a range of data from A1:E1 and I want to copy and paste in the same sheet with incrementing both the column and row (in another word paste them diagonally), anyone can help with this vba?
my current code is this but this code only paste to B2:F2... i want to paste the data until row number 3500.. (with incrementing row and column).. data in A1:E1 is fix, so i would like to paste them to B2:F2, C3:G3, D4:H4 etc..
Sub m1()
Worksheets("Sheet1").Range("A1:E1").Copy
last_row = Worksheets("Sheet1").Range("B" & Worksheets("Sheet1").Rows.Count).End(xlUp).Row + 1
If last_row > 100000 Then last_row = 1
Worksheets("Sheet1").Range("B" & last_row).PasteSpecial
End Sub
There is no build-in function to copy diagonally. You will need to loop over all rows and copy the data individually.
The following piece of code shows you how that could look like
Const MaxRows = 3500
With Worksheets("Sheet1")
Dim r As Range
Set r = .Range("A1:E1")
r.Copy
Dim offset As Long
For offset = 1 To MaxRows
r.offset(offset, offset).PasteSpecial
Next
End With
However, this will be painfully slow. If you just want to copy data, you can change the code to
With Worksheets("Sheet1")
Dim r As Range, data
Set r = .Range("A1:E1")
data = r.Value2
Dim offset As Long
For offset = 1 To MaxRows
r.offset(offset, offset).Value2 = data
Next
End With
Copy Diagonally Using the Range.Copy Method
The Code
Option Explicit
Sub CopyDiagonallyTEST()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
CopyDiagonally wb, "Sheet1", "A1:E1", 3499, 1, 1
' Note that you could omit the last two argument's parameters
' since they are optional and by default equal to 1.
End Sub
Sub CopyDiagonally( _
ByVal wb As Workbook, _
ByVal WorksheetName As String, _
ByVal SourceRangeAddress As String, _
Optional ByVal NumberOfCopies As Long = 1, _
Optional ByVal RowOffset As Long = 1, _
Optional ByVal ColumnOffset As Long = 1)
Const ProcName As String = "CopyDiagonally"
Dim dt As Double: dt = Timer ' measure duration
Dim n As Long
Dim LastAddress As String
Dim AnErrorOccurred
On Error GoTo ClearError
Dim ws As Worksheet: Set ws = wb.Worksheets(WorksheetName)
Dim srg As Range: Set srg = ws.Range(SourceRangeAddress)
Dim drg As Range: Set drg = srg
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
For n = 1 To NumberOfCopies
Set drg = drg.Offset(RowOffset, ColumnOffset)
LastAddress = drg.Address(0, 0) ' keep track in case of an error
srg.Copy drg
Next n
ProcExit:
On Error Resume Next
If Not Application.ScreenUpdating Then
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End If
dt = Timer - dt
Dim tString As String: tString = Format(dt, "0.###") & " seconds"
Dim MsgString As String
MsgString = "Diagonally Copying Stats" & vbLf & vbLf _
& "Source Range Address: '" & SourceRangeAddress & "'" & vbLf _
& "Number of Copies Created: " & n - 1 & " (" _
& NumberOfCopies & ")" & vbLf _
& "Last Range Address: '" & LastAddress & "'" & vbLf _
& "Operation Duration: " & tString
MsgBox MsgString, _
IIf(AnErrorOccurred, vbCritical, vbInformation), ProcName
Debug.Print MsgString
On Error GoTo 0
Exit Sub
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
AnErrorOccurred = True
Resume ProcExit
End Sub
Results in the Immediate Window
Results for CopyDiagonally wb, "Sheet1", "A1:E1", 3499, 1, 1 (Requested)
Diagonally Copying Stats
Source Range Address: 'A1:E1'
Number of Copies Created: 3499 (3499)
Last Range Address: 'EDP3500:EDT3500'
Operation Duration: 34.305 seconds
Results for CopyDiagonally wb, "Sheet1", "A1:E1", 3499, 1, 500
(an error occurs since there are only 16384 columns)
'CopyDiagonally' Run-time error '1004':
Application-defined or object-defined error
Diagonally Copying Stats
Source Range Address: 'A1:E1'
Number of Copies Created: 163 (3499)
Last Range Address: 'XBY164:XCC164'
Operation Duration: 1.375 seconds

Name manager VBA

I'm running a code which basically loops through every sheet of the workbook (containing a huge number of sheets) and copies the text contained in some cells identified through the name manager. Basically, in every sheet there are 4 cells identified through the name manager as "Item_1","Item_2","Item_3","Item_4", as shown in the example.
This is a version of the code
for each anyWS in thisworkbook.worksheets
name=anyWS.Name
num_row=2
for num_row=2 To 4
if workbooks(xx).sheets(name).range("Item_"& num_row).value <> "" then
'the code does something...
else
end if
next
next
In some sheets, though, I have also a fifth item, so that I defined it "Item_5" through the name manager for that sheet. I edited this part of the above code: for num_row=2 To 4 into for num_row=2 To 5, but it fails whenever a sheet doesn't have "Item_5" defined through the name manager, and the code displays an error: the object workbooks(xx).sheets(name).range("Item_"& num_row).value does not exist when num_row=5.
I tried the following:
define automatically "Item_5" through name manager for every sheet in the wb, but it doesn't work.
insert a statement On error goto where the code fails, but unsuccessfully, since when the loop is executed is "stuck" in the error. The code shouldn't stop. Simply, when it doesn't find the range named "Item_5", it should exit the for loop and go to the following sheet.
add a condition like If not (workbooks(xx).sheets(name).range("Item_"& num_row).value) is nothing then, to identify the cases when that name doesn't exist, but it doesn't work.
The third option seems the better to me, so I ask you if there is a way to identify the non-existing object defined through the name manager, in order to prevent that error.
Loop Through Named Cell Ranges
Option Explicit
Sub LoopWorksheets()
Dim ws As Worksheet
Dim Cell As Range
Dim n As Long
For Each ws In ThisWorkbook.Worksheets
For n = 1 To 5
On Error Resume Next
Set Cell = ws.Range("Item_" & n)
On Error GoTo 0
If Not Cell Is Nothing Then ' named cell exists
If Len(CStr(Cell.Value)) > 0 Then ' named cell is not blank
'the code does something...
Debug.Print "The value of cell '" & Cell.Address(0, 0) _
& "' ('Item_" & n & "') in worksheet '" _
& ws.Name & "' is equal to '" & CStr(Cell.Value) & "'."
Else ' named cell is blank
Debug.Print "The cell '" & Cell.Address(0, 0) _
& "' ('Item_" & n & "') in worksheet '" _
& ws.Name & "' is blank."
End If
Set Cell = Nothing
Else ' named cell doesn't exist
Debug.Print "The named cell '" & "Item_" & n _
& "' was not found in worksheet '" & ws.Name & "'."
End If
Next n
Next ws
MsgBox "Looped through worksheets.", vbInformation
End Sub
Edit
You could use a function and do something like the following (simplified).
Sub LoopWorksheetsUsingFunction()
Dim ws As Worksheet
Dim n As Long
Dim RangeName As String
For Each ws In ThisWorkbook.Worksheets
For n = 1 To 5
RangeName = "Item_" & n
If NamedRangeExists(ws, RangeName) Then
If ws.Range(RangeName).Value <> "" Then
' do something
End If
End If
Next n
Next ws
End Sub
Function NamedRangeExists( _
ByVal ws As Worksheet, _
ByVal RangeName As String) _
As Boolean
Dim rg As Range
On Error Resume Next
Set rg = ws.Range(RangeName)
On Error GoTo 0
NamedRangeExists = Not rg Is Nothing
End Function

Select range starting at A2

I found the following code to take the data on the TrialBalance worksheet and convert it into a table. It creates the table and renames it, but the range needs to start at A2 where my table heading are stored.
Sub ConvertTrialBalanceToTable()
Dim wb1 As Workbook
Set wb1 = ActiveWorkbook 'Trial Balance Template File
wb1.Sheets("TrialBalance").Range("A2").CurrentRegion.Select
If ActiveSheet.ListObjects.Count < 1 Then
ActiveSheet.ListObjects.Add.Name = ActiveSheet.Name
End If
End Sub
Convert 'CurrentRegion' to Excel Table When Occupied Rows Above or Columns to the Left of First Cell
If the code is in the TrialBalance template file, use ThisWorkbook instead of ActiveWorkbook.
Sub ConvertTrialBalanceToTable()
With ActiveWorkbook.Worksheets("TrialBalance")
If .ListObjects.Count = 0 Then
.ListObjects.Add(xlSrcRange, _
RefCurrentRegion(.Range("A2")), , xlYes).Name = .Name
End If
End With
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns a reference to the range starting with the first cell
' of a range and ending with the last cell of the first cell's
' Current Region.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefCurrentRegion( _
ByVal FirstCell As Range) _
As Range
Const ProcName As String = "RefCurrentRegion"
On Error GoTo ClearError
If FirstCell Is Nothing Then Exit Function
With FirstCell.Cells(1).CurrentRegion
Set RefCurrentRegion = FirstCell.Resize(.Row + .Rows.Count _
- FirstCell.Row, .Column + .Columns.Count - FirstCell.Column)
End With
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function

How can we find multiple used range addresses in one sheet?

How can we loop through all cells in a sheet and find multiple used range addresses on this one sheet? In this screen shot we have used ranges of B2:F17, I2:M17, Q2:U17, C19:M34, and Q19:U34. How can I identify these beginning and ending cell addresses of these five used ranges, and print them in an array of cells? I have some sample code that shows the total used range on a sheet.
Sub Vba_Used_Range()
Dim iCell As Range
Dim iRange As Range
Dim c As Long
Dim i As Long
Set iRange = ActiveSheet.UsedRange
For Each iCell In ActiveSheet.UsedRange
c = c + 1
If IsEmpty(iCell) = True Then
i = i + 1
End If
Next iCell
MsgBox "There are total " & c & _
" cell(s) in the range, and out of those " & _
i & " cell(s) are empty."
End Sub
Again, how can I print cell addresses for multiple used ranges on one sheet?
Empty Cells in Worksheet Regions
Inspired by userMT.
It is assumed that the regions contain values, not formulas.
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the number of cells and the number of empty cells
' of the worksheet regions containing values in a message box.
' Calls: RefWorksheetValueRegions,CountEmptyCells.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub Vba_Used_Range()
Dim ws As Worksheet: Set ws = ActiveSheet
Dim uarg As Range: Set uarg = RefWorksheetValueRegions(ws)
If uarg Is Nothing Then Exit Sub
Dim ecCount As Long: ecCount = CountEmptyCells(uarg)
MsgBox "There is a total of " & uarg.Cells.Count & _
" cell(s) in the range, and out of those " & _
ecCount & " cell(s) are empty."
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the worksheet regions containing values.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefWorksheetValueRegions( _
ByVal ws As Worksheet) _
As Range
Const ProcName As String = "RefWorksheetValueRegions"
On Error GoTo ClearError
Dim turg As Range
Dim curg As Range
Dim arg As Range
For Each arg In ws.UsedRange.SpecialCells(xlCellTypeConstants).Areas
' Debug.Print "Area: " & arg.Address
If turg Is Nothing Then
Set curg = arg.CurrentRegion
Set turg = curg
Else
If Intersect(arg, curg) Is Nothing Then
Set curg = arg.CurrentRegion
Set turg = Union(turg, curg)
End If
End If
Next arg
If turg Is Nothing Then Exit Function
' For Each arg In turg.Areas
' Debug.Print "Total Area: " & arg.Address
' Next arg
Set RefWorksheetValueRegions = turg
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the number of empty cells of a range.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function CountEmptyCells( _
ByVal mrg As Range) _
As Long
Const ProcName As String = "CountEmptyCells"
On Error GoTo ClearError
Dim arg As Range
Dim ecCount As Long
For Each arg In mrg.Areas
On Error Resume Next
ecCount = ecCount + arg.SpecialCells(xlCellTypeBlanks).Count
On Error GoTo ClearError
Next arg
CountEmptyCells = ecCount
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function
Dim rArea As Range
For Each rArea In ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants+xlCellTypeFormulas).Areas
Debug.Print rArea.Address
Next rArea

Copy, rename and validate success of multiple files and paths in excel

Solution Template SetUp
Been scratching around for the last 5 days here and across the net to find something that works for multiple files. Many a late night/early hours of the morning unsuccessfully piecing together/coding to get a result. Thanks in advance.
The following code is from get-digital-help.com/copyrename-a-file-excel-vba written by Oscar
It works for 1 file, Ive got 8,000 files to do across a deep folder structure so but I'd really like each row to look at a source path, source file name, destination path and destination file:
For each row:
Column A list the source path
Column B lists the source file name
Column C lists to destination path
Column D lists the new file name
Column E writes "Success" or "Fail" validation.
if file name already exists in destination, then "Fail"
If source file doesn't exist, then "Fail"
Nice to have/completely optional!!! :)
Check if source file column A&B exists, = True or False in column F. Where True, then proceed with copy and rename.
If destination file already exist, the fail and column F = duplicate
Leave first row to put in column header names.
Sub CopyRenameFile()
'Dimension variables and declare data types
Dim src As String, dst As String, fl As String
Dim rfl As String
'Save source directory specified in cell A2 to variable src
src = Range("A2")
'Save destination directory specified in cell C2 to variable dst
dst = Range("C2")
'Save file name specified in cell B2 to variable fl
fl = Range("B2")
'Save new file name specified in cell D2 to variable rfl
rfl = Range("D2")
'Enable error handling
On Error Resume Next
'Copy file based on variables src and fl to destination folder based on variable dst and name file based on value in rfl
FileCopy src & "\" & fl, dst & "\" & rfl
'Check if an error has occurred
If Err.Number <> 0 Then
'Show error using message box
MsgBox "Copy error: " & src & "\" & rfl
End If
'Disable error handling
On Error GoTo 0
End Sub
Copy Files Using a File List
This solution consists of three procedures. You run only the first: copyRenameFile. The other two, getOffsetColumn and writeOffsetRange are being called by the first, when necessary.
It is best tested with a new workbook. Insert a module and copy the code into it. Now open your original workbook and copy certain values to e.g. Sheet1 of the new workbook. Since the code is written for Thisworkbook (the workbook containing this code), the original workbook will be safe (will not be written to).
First adjust the values in the constants sections (titled Worksheet and Other). Then test the empty worksheet. Then test with one folder in column A then with more and slowly continue testing with other columns. Possible errors should be suppressed and their messages (descriptions) should appear in VBE's Immediate window (CTRL+G).
As a byproduct of this investigation, I've also added the createFolders function to create folders in one case when MkDir 'cannot', and two procedures to test it.
The Code
Option Explicit
Sub copyRenameFile()
' Initialize error handling.
Const ProcName As String = "copyRenameFile"
On Error GoTo clearError ' Turn on error trapping.
' Worksheet
Const wsName As String = "Sheet1" ' Worksheet Name
Const FirstRow As Long = 2 ' First Row Number
Const LastRowCol As Variant = "A" ' Last Row Column Index
Dim srcCols As Variant ' Source Columns Array
srcCols = VBA.Array("A", "B", "C", "D")
Dim tgtCols As Variant ' Target Columns Array
tgtCols = VBA.Array("E", "F")
' Other
Dim filMsg() As Variant ' File Messages
filMsg = VBA.Array("Fail", "Success")
Dim folMsg() As Variant ' Folder Messages
folMsg = VBA.Array(False, True, "Duplicate")
Dim PathDelimiter As String
PathDelimiter = Application.PathSeparator
Dim wb As Workbook
Set wb = ThisWorkbook ' 'Thisworkbook' is the workbook containing this code.
' Define Last Row Column Range ('rng').
Dim ws As Worksheet
Set ws = wb.Worksheets(wsName)
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, LastRowCol).End(xlUp).Row
If LastRow < FirstRow Then
GoTo FirstRowBelowLastRow
End If
Dim rng As Range
Set rng = ws.Range(ws.Cells(FirstRow, LastRowCol), _
ws.Cells(LastRow, LastRowCol))
' Write Source Column Ranges to Source Jagged Array ('Source').
Dim ubcS As Long
ubcS = UBound(srcCols)
Dim Source As Variant
ReDim Source(0 To ubcS)
Dim Data As Variant
Dim j As Long
For j = 0 To ubcS
getOffsetColumn Data, srcCols(j), rng
Source(j) = Data
Next j
' Define Target Jagged Array ('Target').
Dim ubcT As Long
ubcT = UBound(tgtCols)
Dim ubs As Long
ubs = UBound(Source(0))
Dim Target As Variant
ReDim Target(0 To ubcT)
ReDim Data(1 To ubs, 1 To 1)
For j = 0 To ubcT
Target(j) = Data
Next j
' Declare additional variables for the For Next loop.
Dim i As Long
Dim Copied As Long
Dim srcPath As String
Dim tgtPath As String
' Loop through rows of arrays of Source Jagged Array, check folders,
' check files and finally copy if condition is met. At the same time
' write results to arrays of Target Jagged Array.
' The condition to copy is met when source file exists,
' and target file does not.
For i = 1 To ubs
' Folders
srcPath = Source(0)(i, 1)
If Dir(srcPath, vbDirectory) = "" Then
' Source Folder and Source File do not exist.
Target(0)(i, 1) = filMsg(0)
Target(1)(i, 1) = folMsg(0)
GoTo NextRow
End If
' Source Folder exists.
tgtPath = Source(1)(i, 1)
If Dir(tgtPath, vbDirectory) = "" Then
' Target Folder and Target File do not exist.
Target(0)(i, 1) = filMsg(0)
Target(1)(i, 1) = folMsg(0)
GoTo NextRow
End If
' Source Folder and Target Folder exist.
' Files
srcPath = srcPath & PathDelimiter & Source(2)(i, 1)
If Dir(srcPath) = "" Then
' Source File does not exist.
Target(0)(i, 1) = filMsg(0)
Target(1)(i, 1) = folMsg(0)
GoTo NextRow
End If
' Source File exists.
tgtPath = tgtPath & PathDelimiter & Source(3)(i, 1)
If Dir(tgtPath) <> "" Then
' Target File exists.
Target(0)(i, 1) = filMsg(0)
Target(1)(i, 1) = folMsg(2)
GoTo NextRow
End If
' Source File exists and Target File does not.
Target(0)(i, 1) = filMsg(1)
Target(1)(i, 1) = folMsg(1)
' Copy
FileCopy srcPath, tgtPath
' Count files copied.
Copied = Copied + 1
NextRow:
Next i
' Write values (results) from arrays of Target Jagged Array
' to Target Columns.
For j = 0 To ubcT
writeOffsetRange Target(j), tgtCols(j), rng
Next j
' Inform user.
MsgBox "Copied " & Copied & " files.", vbInformation, "Success"
ProcExit:
Exit Sub
FirstRowBelowLastRow:
Debug.Print "'" & ProcName & "': First row below last row."
GoTo ProcExit
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
End Sub
Sub getOffsetColumn(ByRef Data As Variant, _
OffsetColumnIndex As Variant, _
ColumnRange As Range)
' Initialize error handling.
Const ProcName As String = "getOffsetColumn"
On Error GoTo clearError ' Turn on error trapping.
Data = Empty
If ColumnRange Is Nothing Then
GoTo NoRange
End If
Dim ws As Worksheet
Set ws = ColumnRange.Worksheet
If ColumnRange.Rows.Count > 1 Then
Data = ColumnRange.Offset(, ws.Columns(OffsetColumnIndex).Column _
- ColumnRange.Column) _
.Value
Else
ReDim Data(1 To 1, 1 To 1)
Data(1, 1) = ColumnRange.Offset(, ws.Columns(OffsetColumnIndex) _
.Column _
- ColumnRange.Column) _
.Value
End If
ProcExit:
Exit Sub
NoRange:
Debug.Print "'" & ProcName & "': No Range."
GoTo ProcExit
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
End Sub
Sub writeOffsetRange(Data As Variant, _
OffsetColumnIndex As Variant, _
ColumnRange As Range)
' Initialize error handling.
Const ProcName As String = "writeOffsetColumn"
On Error GoTo clearError ' Turn on error trapping.
If ColumnRange Is Nothing Then
GoTo NoRange
End If
Dim ws As Worksheet
Set ws = ColumnRange.Worksheet
ColumnRange.Offset(, ws.Columns(OffsetColumnIndex).Column _
- ColumnRange.Column).Value = Data
ProcExit:
Exit Sub
NoRange:
Debug.Print "'" & ProcName & "': No Range."
GoTo ProcExit
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
End Sub
The Byproduct
' e.g. "C:\Test" is an existing folder, "C:\Test\Test1" is not.
' When you want to create the folder "C:\Test\Test1\Test2", 'MkDir' will return
' "Run-time error '76': Path Not found", because "C:\Test\Test1" does not exist.
' The 'createFolders' function remedies this by creating as many folders
' as needed. In the previous example it first creates "C:\Test\Test1" and
' only then creates "C:\Test\Test1\Test2" in it.
' The function returns 'True' if the folder previously existed or now exists.
' The function returns 'False' if 'PathString' is invalid.
Function createFolders(PathString As String) As Boolean
' Initialize error handling.
Const ProcName As String = "createFolders"
On Error GoTo clearError ' Turn on error trapping.
' Split Path String ('PathString') by System Path Separator ('Delimiter')
' into 1D zero-based Folders Array 'Folders()'.
Dim Delimiter As String
Delimiter = Application.PathSeparator
Dim Folders() As String
Folders = Split(PathString, Delimiter)
' Define Last Subscript ('LastSS') to be considered, because Path String
' could be ending with a System Path Separator.
Dim LastSS As Long
LastSS = UBound(Folders)
If Folders(LastSS) = "" Then
LastSS = LastSS - 1
End If
' Using Folders Array, write paths to Paths Array ('Paths()').
Dim Paths() As String
ReDim Paths(0 To LastSS)
Paths(0) = Folders(0)
Dim j As Long
If LastSS > 0 Then
For j = 1 To LastSS
Paths(j) = Paths(j - 1) & Delimiter & Folders(j)
Next j
End If
' Create each folder if it does not exist.
For j = 0 To LastSS
If Dir(Paths(j), vbDirectory) = "" Then
MkDir Paths(j)
End If
Next j
' Write result.
createFolders = True
ProcExit:
Exit Function
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
End Function
Sub testCreateFolders()
Const PathString As String = "C:\Test\Test1\Test2"
Dim Result As Boolean
Result = createFolders(PathString)
If Result Then
MsgBox "If the path previously didn't exist, now it certainly does."
Else
MsgBox "The supplied path is invalid."
End If
End Sub
Sub testMkDir()
Const PathString As String = "C:\Test\Test1\Test2"
MkDir PathString
End Sub

Resources