How to combine workbooks from their actual start values (no appending) - vba

I’m really sorry if this is a silly question but the macro I’m currently using keeps appending the new workbooks data when I’m combining them.
Ideally, I want the new workbook to be beside it at cells AA1 instead of directly appending like it is in the picture. I’m sorry I can’t be much more help. I’ve been going through it trying to get it to start for the other workbooks to not append but write where it actually is from the other workbooks. No luck so far.
I’m sure I’ll get there in a few hours or so, but thanks again if you’re willing to assist.
Option Explicit
Public Sub CombineManyWorkbooksIntoOneWorksheet()
Dim strDirContainingFiles As String, strFile As String, _
strFilePath As String
Dim wbkDst As Workbook, wbkSrc As Workbook
Dim wksDst As Worksheet, wksSrc As Worksheet
Dim lngIdx As Long, lngSrcLastRow As Long, _
lngSrcLastCol As Long, lngDstLastRow As Long, _
lngDstLastCol As Long, lngDstFirstFileRow As Long
Dim rngSrc As Range, rngDst As Range, rngFile As Range
Dim colFileNames As Collection
Set colFileNames = New Collection
'Set references up-front
strDirContainingFiles = "C:\Users\Guide\Projects\" '<~ your folder
Set wbkDst = Workbooks.Add '<~ Dst is short for destination
Set wksDst = wbkDst.ActiveSheet
'Store all of the file names in a collection
strFile = Dir(strDirContainingFiles & "\*.xlsm")
Do While Len(strFile) > 0
colFileNames.Add Item:=strFile
strFile = Dir
Loop
''CHECKPOINT: make sure colFileNames has the file names
'Dim varDebug As Variant
'For Each varDebug In colFileNames
' Debug.Print varDebug
'Next varDebug
'Now we can start looping through the "source" files
'and copy their data to our destination sheet
For lngIdx = 1 To colFileNames.Count
'Assign the file path
strFilePath = strDirContainingFiles & "\" & colFileNames(lngIdx)
'Open the workbook and store a reference to the data sheet
Set wbkSrc = Workbooks.Open(strFilePath)
Set wksSrc = wbkSrc.Worksheets("Sheet1")
'Identify the last row and last column, then
'use that info to identify the full data range
lngSrcLastRow = LastOccupiedRowNum(wksSrc)
lngSrcLastCol = LastOccupiedColNum(wksSrc)
With wksSrc
Set rngSrc = .Range(.Cells(1, 1), .Cells(lngSrcLastRow, _
lngSrcLastCol))
End With
''CHECKPOINT: make sure we have the full source data range
'wksSrc.Range("A1").Select
'rngSrc.Select
'If this is the first (1st) loop, we want to keep
'the header row from the source data, but if not then
'we want to remove it
If lngIdx <> 1 Then
Set rngSrc = rngSrc.Offset(1, 0).Resize(rngSrc.Rows.Count - 1)
End If
''CHECKPOINT: make sure that we remove the header row
''from the source range on every loop that is not
''the first one
'wksSrc.Range("A1").Select
'rngSrc.Select
'Copy the source data to the destination sheet, aiming
'for cell A1 on the first loop then one past the
'last-occupied row in column A on each following loop
If lngIdx = 1 Then
lngDstLastRow = 1
Set rngDst = wksDst.Cells(1, 1)
Else
lngDstLastRow = LastOccupiedRowNum(wksDst)
Set rngDst = wksDst.Cells(lngDstLastRow + 1, 1)
End If
rngSrc.Copy Destination:=rngDst '<~ this is the copy / paste
'Almost done! We want to add the source file info
'for each of the data blocks to our destination
'On the first loop, we need to add a "Source Filename" column
If lngIdx = 1 Then
lngDstLastCol = LastOccupiedColNum(wksDst)
wksDst.Cells(1, lngDstLastCol + 1) = "Source Filename"
End If
'Identify the range that we need to write the source file
'info to, then write the info
With wksDst
'The first row we need to write the file info to
'is the same row where we did our initial paste to
'the destination file
lngDstFirstFileRow = lngDstLastRow + 1
'Then, we need to find the NEW last row on the destination
'sheet, which will be further down (since we pasted more
'data in)
lngDstLastRow = LastOccupiedRowNum(wksDst)
lngDstLastCol = LastOccupiedColNum(wksDst)
'With the info from above, we can create the range
Set rngFile = .Range(.Cells(lngDstFirstFileRow, lngDstLastCol), _
.Cells(lngDstLastRow, lngDstLastCol))
''CHECKPOINT: make sure we have correctly identified
''the range where our file names will go
'wksDst.Range("A1").Select
'rngFile.Select
'Now that we have that range identified,
'we write the file name
rngFile.Value = wbkSrc.Name
End With
'Close the source workbook and repeat
wbkSrc.Close SaveChanges:=False
Next lngIdx
'Let the user know that the combination is done!
''MsgBox "Data combined!"
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'INPUT : Sheet, the worksheet we'll search to find the last row
'OUTPUT : Long, the last occupied row
'SPECIAL CASE: if Sheet is empty, return 1
Public Function LastOccupiedRowNum(Sheet As Worksheet) As Long
Dim lng As Long
If Application.WorksheetFunction.CountA(Sheet.Cells) <> 0 Then
With Sheet
lng = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
End With
Else
lng = 1
End If
LastOccupiedRowNum = lng
End Function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'INPUT : Sheet, the worksheet we'll search to find the last column
'OUTPUT : Long, the last occupied column
'SPECIAL CASE: if Sheet is empty, return 1
Public Function LastOccupiedColNum(Sheet As Worksheet) As Long
Dim lng As Long
If Application.WorksheetFunction.CountA(Sheet.Cells) <> 0 Then
With Sheet
lng = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
End With
Else
lng = 1
End If
LastOccupiedColNum = lng
End Function

You should identify the individual tasks that you program will have to perform and create methods and functions to handle these smaller tasks. Doing this will allow you to easily debug your code.
get1stCellInNextColumn - Defines the Target range
getFileCollection - Collect all the file paths
getLastUsedCell - Gets the last used cell in a range. Used to define both Source and Target ranges
getSourceRange - Gets the Source Range
InsertData - Opens a Source file and copies its data to a Target Workbook
Main_CombineManyWorkbooksIntoOneWorksheet - Process all the files
Option Explicit
'This is the Main function that combines all the other Subs and Functions together to process the data
Public Sub Main_CombineManyWorkbooksIntoOneWorksheet()
Application.ScreenUpdating = False
Const FOLDERNAME As String = "C:\Users\best buy\Downloads\_Temp\" ' "C:\Users\Guide\"
Const EXTENSION As String = "\*.xlsx" '"\*.xlsm"
Dim cFiles As Collection
Dim x As Long
Set cFiles = getFileCollection(FOLDERNAME, EXTENSION)
With Workbooks.Add
For x = 1 To cFiles.Count
InsertData cFiles.Item(x), .Worksheets(1)
Next
End With
Application.ScreenUpdating = True
End Sub
'Opens Source Workbook, Copies Data to Target Worksheet and then closes the Source Workbook
Public Sub InsertData(SourceWBName As String, TargetWS As Worksheet)
Dim rSource As Range
With Workbooks.Open(SourceWBName)
Set rSource = getSourceRange(.Worksheets("Sheet1"))
If rSource Is Nothing Then
Debug.Print .FullName, "No Data Found"
Else
rSource.Copy get1stCellInNextColumn(TargetWS.UsedRange)
End If
.Close SaveChanges:=False
End With
End Sub
'Collects the full file paths for the Source workbooks
Function getFileCollection(FOLDERNAME As String, FileExtension As String) As Collection
Dim FileName As String
Dim col As Collection
Set col = New Collection
'Store all of the file names in a collection
FileName = Dir(FOLDERNAME & FileExtension)
Do While Len(FileName) > 0
col.Add Item:=FOLDERNAME & FileName
FileName = Dir
Loop
Set getFileCollection = col
End Function
'Gets the Source range from a Worksheet
Function getSourceRange(xlWS As Worksheet) As Range
Dim rLastCell As Range
With xlWS
Set rLastCell = getLastUsedCell(.UsedRange, True, True)
If Not rLastCell Is Nothing Then Set getSourceRange = .Range(.Cells(1, 1), rLastCell)
End With
End Function
'Gets the first cell in the next unused Column of the Target range
Function get1stCellInNextColumn(Target As Range) As Range
Dim r As Range
'Get last used cell in last used column of the Target range
Set r = getLastUsedCell(Target, False, True)
If r Is Nothing Then
Set r = Target.EntireColumn.Cells(1, 1)
Else
'Get the first cell in the next column adjacent to the Target range
Set r = Target.Columns(Target.Columns.Count).Next
End If
Set get1stCellInNextColumn = r
End Function
'Gets the last used cell the last used row
'Or the last used cell the last used column
'Or the last used cells in the Target range
Function getLastUsedCell(Target As Range, InRow As Boolean, InColumn As Boolean) As Range
Dim rRow As Range, rColumn As Range
If Target Is Nothing Then Exit Function
With Target
Set rRow = .Find(What:="*", After:=.Range("A1"), LookAt:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)
If rRow Is Nothing Then Exit Function
Set rColumn = .Find(What:="*", After:=.Range("A1"), LookAt:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
If InRow And InColumn Then 'Get last used cell in last used column
Set getLastUsedCell = Intersect(rRow.EntireRow, rColumn.EntireColumn)
ElseIf InRow Then 'Get last used cell in last used row
Set getLastUsedCell = rRow
ElseIf InColumn Then 'Get last used cell in last used column
Set getLastUsedCell = rColumn
End If
End With
End Function

Sorry to answer my own question but I figured it out after a bit of debugging. The following code below does not append and creates the excels exactly as you need them. Change the values as you see fit or use original code if you need to append.
Option Explicit
Public Sub CombineManyWorkbooksIntoOneWorksheet()
Dim strDirContainingFiles As String, strFile As String, _
strFilePath As String
Dim wbkDst As Workbook, wbkSrc As Workbook
Dim wksDst As Worksheet, wksSrc As Worksheet
Dim lngIdx As Long, lngSrcLastRow As Long, _
lngSrcLastCol As Long, lngDstLastRow As Long, _
lngDstLastCol As Long, lngDstFirstFileRow As Long
Dim rngSrc As Range, rngDst As Range, rngFile As Range
Dim colFileNames As Collection
Set colFileNames = New Collection
'Set references up-front
strDirContainingFiles = "C:\Users\Guide\" '<~ your folder
Set wbkDst = Workbooks.Add '<~ Dst is short for destination
Set wksDst = wbkDst.ActiveSheet
'Store all of the file names in a collection
strFile = Dir(strDirContainingFiles & "\*.xlsm")
Do While Len(strFile) > 0
colFileNames.Add Item:=strFile
strFile = Dir
Loop
''CHECKPOINT: make sure colFileNames has the file names
'Dim varDebug As Variant
'For Each varDebug In colFileNames
' Debug.Print varDebug
'Next varDebug
'Now we can start looping through the "source" files
'and copy their data to our destination sheet
For lngIdx = 1 To colFileNames.Count
'Assign the file path
strFilePath = strDirContainingFiles & "\" & colFileNames(lngIdx)
'Open the workbook and store a reference to the data sheet
Set wbkSrc = Workbooks.Open(strFilePath)
Set wksSrc = wbkSrc.Worksheets("Sheet1")
'Identify the last row and last column, then
'use that info to identify the full data range
lngSrcLastRow = LastOccupiedRowNum(wksSrc)
lngSrcLastCol = LastOccupiedColNum(wksSrc)
With wksSrc
Set rngSrc = .Range(.Cells(1, 1), .Cells(lngSrcLastRow, _
lngSrcLastCol))
End With
''CHECKPOINT: make sure we have the full source data range
'wksSrc.Range("A1").Select
'rngSrc.Select
'If this is the first (1st) loop, we want to keep
'the header row from the source data, but if not then
'we want to remove it
''CHECKPOINT: make sure that we remove the header row
''from the source range on every loop that is not
''the first one
'wksSrc.Range("A1").Select
'rngSrc.Select
'Copy the source data to the destination sheet, aiming
'for cell A1 on the first loop then one past the
'last-occupied row in column A on each following loop
If lngIdx = 1 Then
lngDstLastRow = 1
Set rngDst = wksDst.Cells(1, 1)
Else
lngDstLastRow = 1
Set rngDst = wksDst.Cells(1, 20)
End If
rngSrc.Copy Destination:=rngDst '<~ this is the copy / paste
'Close the source workbook and repeat
wbkSrc.Close SaveChanges:=False
Next lngIdx
'Let the user know that the combination is done!
''MsgBox "Data combined!"
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'INPUT : Sheet, the worksheet we'll search to find the last row
'OUTPUT : Long, the last occupied row
'SPECIAL CASE: if Sheet is empty, return 1
Public Function LastOccupiedRowNum(Sheet As Worksheet) As Long
Dim lng As Long
If Application.WorksheetFunction.CountA(Sheet.Cells) <> 0 Then
With Sheet
lng = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
End With
Else
lng = 1
End If
LastOccupiedRowNum = lng
End Function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'INPUT : Sheet, the worksheet we'll search to find the last column
'OUTPUT : Long, the last occupied column
'SPECIAL CASE: if Sheet is empty, return 1
Public Function LastOccupiedColNum(Sheet As Worksheet) As Long
Dim lng As Long
If Application.WorksheetFunction.CountA(Sheet.Cells) <> 0 Then
With Sheet
lng = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
End With
Else
lng = 1
End If
LastOccupiedColNum = lng
End Function

Related

Copying all data from a table in one tab into another tab in the same workbook based on 2 input boxes which will define a date range

i have tried many sites and am really struggling as i cant seem to understand the VBA code
tab1 = data from C8:Rx? ... the data will continously grow so table will get bigger all the time
Column C in tab1 contains dates 21/05/2021
I want to be able to have 2 prompt boxes where i enter a date from and date to 21/05/2021 - 22/05/2021
when i action the macro it will take only the data from the table in tab1 in between these dates
and paste them in tab2 at cell ref c8 (the start of the table)
Option Explicit
'This subroutine prompts the user to select dates
Public Sub PromptUserForInputDates()
Dim strStart As String, strEnd As String, strPromptMessage As String
'Prompt the user to input the start date
strStart = InputBox("Please enter the start date")
'Validate the input string
If Not IsDate(strStart) Then
strPromptMessage = "Oops! It looks like your entry is not a valid " & _
"date. Please retry with a valid date..."
MsgBox strPromptMessage
Exit Sub
End If
'Prompt the user to input the end date
strEnd = InputBox("Please enter the end date")
'Validate the input string
If Not IsDate(strStart) Then
strPromptMessage = "Oops! It looks like your entry is not a valid " & _
"date. Please retry with a valid date..."
MsgBox strPromptMessage
Exit Sub
End If
'Call the next subroutine, which will do produce the output workbook
Call CreateSubsetWorkbook(strStart, strEnd)
End Sub
'This subroutine creates the new workbook based on input from the prompts
Public Sub CreateSubsetWorkbook(StartDate As String, EndDate As String)
Dim wbkOutput As Workbook
Dim wksOutput As Worksheet, wks As Worksheet
Dim lngLastRow As Long, lngLastCol As Long, lngDateCol As Long
Dim rngFull As Range, rngResult As Range, rngTarget As Range
'Set references up-front
lngDateCol = 3 '<~ we know dates are in column C
Set wbkOutput = Workbooks.Add
'Loop through each worksheet
For Each wks In ThisWorkbook.Worksheets
With wks
'Create a new worksheet in the output workbook
Set wksOutput = wbkOutput.Sheets.Add
wksOutput.Name = wks.Name
'Create a destination range on the new worksheet that we
'will copy our filtered data to
Set rngTarget = wksOutput.Cells(1, 1)
'Identify the data range on this sheet for the autofilter step
'by finding the last row and the last column
lngLastRow = .Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
lngLastCol = .Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
Set rngFull = .Range(.Cells(1, 1), .Cells(lngLastRow, lngLastCol))
'Apply a filter to the full range to get only rows that
'are in between the input dates
With rngFull
.AutoFilter Field:=lngDateCol, _
Criteria1:=">=" & StartDate, _
Criteria2:="<=" & EndDate
'Copy only the visible cells and paste to the
'new worksheet in our output workbook
Set rngResult = rngFull.SpecialCells(xlCellTypeVisible)
rngResult.copy Destination:=rngTarget
End With
'Clear the autofilter safely
.AutoFilterMode = False
If .FilterMode = True Then
.ShowAllData
End If
End With
Next wks
'Let the user know our macro has finished!
MsgBox "Data transferred!"
End Sub
What this solution does:
Assumes your dates are in Column A of your worksheet.
Can be used to replace the CreateSubsetWorkbook sub you have.
You can still use the PromptUserForInputDates and then call this sub instead of CreateSubsetWorkbook.
Public Sub FillOutputRange(ByVal StartDate As Date, ByVal EndDate As Date)
Dim SourceSheet As Worksheet
Dim DestinationSheet As Worksheet
Dim TargetCell As Range
Dim LastRow As Long
Dim StartRow As Long
Dim EndRow As Long
Dim RowLoopCounter As Long
Dim EndColumn As Long
Dim OutputDataArray As Variant
With ThisWorkbook
Set SourceSheet = .Sheets("Sheet1") 'Change this to the name of your source sheet
Set DestinationSheet = .Sheets("Sheet2") 'Change this to the name of your destination sheet
End With
With SourceSheet
LastRow = .Cells(Rows.Count, "A").End(xlUp).Row
For Each TargetCell In .Range("A1:A" & LastRow)
If TargetCell.Value = StartDate Then
StartRow = TargetCell.Row
Exit For
End If
Next TargetCell
If StartRow = 0 Then MsgBox "Start Date was not found", vbOKOnly, "No Start Date": Exit Sub
For RowLoopCounter = LastRow To StartRow Step -1
If .Range("C" & RowLoopCounter).Value = EndDate Then
EndRow = RowLoopCounter
Exit For
End If
Next RowLoopCounter
If EndRow = 0 Then MsgBox "End Date was not found", vbOKOnly, "No End Date": Exit Sub
EndColumn = .Cells(EndRow, .Columns.Count).End(xlToLeft).Column
OutputDataArray = .Range(.Cells(StartRow, "A"), .Cells(EndRow, EndColumn)).Value
End With
With DestinationSheet
.Range("C8").Resize(UBound(OutputDataArray, 1), UBound(OutputDataArray, 2)).Value = OutputDataArray
End With
End Sub
How does it work?
The flow of this sub can be described as:
First we set variables to use as reference to our SourceSheet and DestinationSheet - Be sure to change these to the correct worksheet names for your workbook.
Then with the SourceSheet we find the LastRow - see "Better way to find last row"
Then we use a For Each Next Statement to loop through each cell in the Range("A1:A" & LastRow") - If LastRow is say, 10 then this would be equivalent to Range("A1:A10")
With each iteration of the loop we are checking if the cell's value matches our StartDate argument passed from the PromptUserForInputDates Sub.
Once we have our first match, we assign the Row of that cell to the StartRow variable and the loop is exited and the code continues.
The next loop is a For Next Statement which works with slightly different syntax. I've used this to demonstrate using the different statement. We loop backwards from the end of the range, back to the StartRow so this way the EndRow will be established from the last occurrence of the EndDate in your range.
Now that we have the StartRow and EndRow we know which rows the target data are in between (inclusive).
EndColumn is found based on the last column with data in the EndRow - you can find this based on any row, I just chose to find it with the end row.
Using the 3 variables, StartRow, EndRow, and EndColumn we can build our OutputDataArray by assigning the value of the target range to the array variable. This automatically builds a two dimensional array with all the data in it.
Finally, with our DestinationSheet we now write the array to a range in the sheet. I've hard coded this to start at Range("C8") per your question.
The Range.Resize Property is used to change the Range size to match the Array size, this way the data from the Array writes directly into the sheet.
This Chip Pearson article is great for learning about Arrays.
Note: I've added If...Then statements after each loop to catch errors that will appear if either of the variables StartRow and/or EndRow are not assigned (meaning they retain their default value of 0). This handles the error by throwing a messagebox to the user advising whichever date hasn't been found.
Demonstration
Based on the following dates being used:
StartDate = 3/6/2021
EndDate = 6/6/2021
Sample Source data:
Outcome of running sub:
my code error
Error says application defined or object defined error
Sorry for all the trouble #samuel
Option Explicit
'This subroutine prompts the user to select dates
Public Sub PromptUserForInputDates()
Dim strStart As String, strEnd As String, strPromptMessage As String
'Prompt the user to input the start date
strStart = InputBox("Please enter the start date")
'Validate the input string
If Not IsDate(strStart) Then
strPromptMessage = "Oops! It looks like your entry is not a valid " & _
"date. Please retry with a valid date..."
MsgBox strPromptMessage
Exit Sub
End If
'Prompt the user to input the end date
strEnd = InputBox("Please enter the end date")
'Validate the input string
If Not IsDate(strStart) Then
strPromptMessage = "Oops! It looks like your entry is not a valid " & _
"date. Please retry with a valid date..."
MsgBox strPromptMessage
Exit Sub
End If
'Call the next subroutine, which will do produce the output workbook
Call FillOutputRange(strStart, strEnd)
End Sub
Public Sub FillOutputRange(ByVal StartDate As Date, ByVal EndDate As Date)
Dim SourceSheet As Worksheet
Dim DestinationSheet As Worksheet
Dim TargetCell As Range
Dim LastRow As Long
Dim StartRow As Long
Dim EndRow As Long
Dim RowLoopCounter As Long
Dim EndColumn As Long
Dim OutputDataArray As Variant
With ThisWorkbook
Set SourceSheet = .Sheets("Branches consolidated Master (4") 'Change this to the name of your source sheet
Set DestinationSheet = .Sheets("Date Extract (5)") 'Change this to the name of your destination sheet
End With
With SourceSheet
LastRow = .Cells(Rows.Count, "C").End(xlUp).Row
For Each TargetCell In .Range("C8:C" & LastRow)
If TargetCell.Value = StartDate Then
StartRow = TargetCell.Row
Exit For
End If
Next TargetCell
For RowLoopCounter = LastRow To StartRow Step -1
If Range("C" & RowLoopCounter).Value = EndDate Then
EndRow = RowLoopCounter
Exit For
End If
Next RowLoopCounter
EndColumn = .Cells(EndRow, .Columns.Count).End(xlToLeft).Column
OutputDataArray = .Range(.Cells(StartRow, "A"), .Cells(EndRow, EndColumn)).Value
End With
With DestinationSheet
.Range("C8").Resize(UBound(OutputDataArray, 1), UBound(OutputDataArray, 2)).Value = OutputDataArray
End With
End Sub

Excel VBA Gathering Names from a Schedule of People Working Today

Even though I thought this would be a common question, I have searched and can't seem to find an answer.
I have a work schedule and I'd like to search the today's date and return all the names of those who are scheduled to work today. I built a code that works but it takes a long time to complete and is not 100% effective every time its run. I am sure there must be a better and faster way to do this but I have not yet been able to find one. I have it broken apart into two different codes. The first one finds the column where today's date is located and the second one gathers the names and places them onto the next sheet.
Here's the First Sub:
Sub GetDateRow_()
'//////Finds the row that has today's date and returns the value of that row to cell C34\\\\\\
Dim SearchMe As Integer
SearchMe = Sheets("Sheet1").Range("C33")
Set FindMe = Sheets("Sheet1").Range("C5:AD5").Find(What:=SearchMe, LookIn:=xlValues, LookAt:= _
xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False)
Sheets("Sheet1").Range("C34").Value = Cells(1, FindMe.Column)
End Sub
And the Second Sub:
Sub CopyScheduledToList()
'//////Searches Today's day Column from the schedule given by GetDateRow Sub & assimbles \\\\\\\
'////// the names of those who are scheduled to work today to a list on Page 2 \\\\\\\
Dim Ccount As Integer
Dim lngLoop As Long
Dim RowCount As Integer
Dim dShift As String
Dim cShift As String
Ccount = 1
dShift = "A63"
cShift = "TLA"
RowCount = Sheets("Sheet1").Range("C34").Value
lngLoop = 1
For lngLoop = 1 To Rows.count
If Cells(lngLoop, RowCount).Value = cShift Then Worksheets("Sheet2").Cells(1, 4).Value = Worksheets("Sheet1").Cells(lngLoop, 1).Value
'////// Get's the Team Leader and places name into column D on Page 2
If Cells(lngLoop, RowCount).Value = dShift Then Worksheets("Sheet2").Cells(Ccount, 1).Value = Worksheets("Sheet1").Cells(lngLoop, 1).Value
If Worksheets("Sheet2").Range("A" & Ccount).Value <> "" Then Ccount = Ccount + 1
'////// Places the name from the working list onto page 2 and adds a count so the next value found
'////// will go to the next empty row on Sheet 2
Next lngLoop
End Sub
Again, your help is greatly appreciated!
Your excessively long run times will be due to looping over 1048576 cells in your For lngLoop = 1 To Rows.count loop. That can be improved by just processing as far as the last cell that contains data in the applicable column.
The problem of it not always working correctly is almost certainly due to the fact that you have some Cells references which aren't qualified with the sheet that you want to use, and therefore they refer to the ActiveSheet.
Sub GetDateRow_()
'//////Finds the row that has today's date and returns the value of that row to cell C34\\\\\\
Dim SearchMe As Date
Dim FindMe As Range
With Worksheets("Sheet1")
SearchMe = .Range("C33").Value
Set FindMe = .Range("C5:AD5").Find(What:=SearchMe, _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If FindMe Is Nothing Then
MsgBox "Date not found!"
Else
'I think this line
'.Range("C34").Value = .Cells(1, FindMe.Column).Value
'should be
.Range("C34").Value = FindMe.Column
'so that it saves the column number you want
End If
End With
End Sub
Sub CopyScheduledToList()
'//////Searches Today's day Column from the schedule given by GetDateRow Sub & assembles \\\\\\\
'////// the names of those who are scheduled to work today to a list on Page 2 \\\\\\\
Dim Ccount As Integer
Dim lngLoop As Long
Dim TodaysCol As Long
Dim dShift As String
Dim cShift As String
Dim lastRow As Long
Dim wsSrc As Worksheet
Dim wsDst As Worksheet
Ccount = 1
dShift = "A63"
cShift = "TLA"
Set wsSrc = Worksheets("Sheet1")
Set wsDst = Worksheets("Sheet2")
TodaysCol = wsSrc.Range("C34").Value
'Find last used row in today's column
lastRow = wsSrc.Cells(wsSrc.Rows.Count, TodaysCol).End(xlUp).Row
For lngLoop = 1 To lastRow
If wsSrc.Cells(lngLoop, TodaysCol).Value = cShift Then
wsDst.Cells(1, "D").Value = wsSrc.Cells(lngLoop, "A").Value
End If
'////// Get's the Team Leader and places name into column D on Page 2
If wsSrc.Cells(lngLoop, TodaysCol).Value = dShift Then
If wsSrc.Cells(lngLoop, "A").Value <> "" Then
wsDst.Cells(Ccount, "A").Value = wsSrc.Cells(lngLoop, "A").Value
Ccount = Ccount + 1
End If
End If
'////// Places the name from the working list onto page 2 and adds a count so the next value found
'////// will go to the next empty row on Sheet 2
Next lngLoop
End Sub
Your first sub is, in fact, a function (or should be) which returns a value which can be assigned to a variable in your second sub. The following code realises that concept. It is different in other respects too, but I think you will like it. Please try.
Option Explicit
Sub CopyScheduledToList()
'//////Searches Today's day Column from the schedule given by GetDateRow Sub & assambles \\\\\\\
'////// the names of those who are scheduled to work today to a list on Page 2 \\\\\\\
' it is best practise, not to have any hard-coded addresses in the code.
' Therefore it is good to place all parameters separate from and before the code:
Const SearchRow As Long = 5
Const dShift As String = "A63"
Const cShift As String = "TLA"
Dim WsMain As Worksheet ' better to set your own variable
Dim WsOutput As Worksheet ' even if it will be "ActiveSheet"
Dim TgtColumn As Long
Dim Rlast As Long ' last data row in WsMain
Dim Rcount As Long ' output row counter
Dim R As Long
Set WsMain = ActiveSheet ' might be Sheets("Sheet1")
Set WsOutput = Worksheets("Sheet2") ' or, simply, Sheet1
TgtColumn = DateColumn(WsMain, SearchRow)
If TgtColumn < 1 Then Exit Sub
Rcount = 1
With WsMain
Rlast = .Cells(.Rows.Count, TgtColumn).End(xlUp).Row
For R = 1 To Rlast
Select Case .Cells(R, TgtColumn).value
Case cShift
'////// Get's the Team Leader and places name into column D on WsOutput
WsOutput.Cells(Rcount, "D").value = .Cells(R, 1).value
Case dShift
WsOutput.Cells(Rcount, "A").value = .Cells(R, 1).value
Case Else
Exit Sub ' define the correct response if neither is found
End Select
If Len(WsOutput.Cells(Rcount, "A").value) Then Rcount = Rcount + 1
'////// Places the name from the working list onto page 2 and adds a count so the next value found
'////// will go to the next empty row on Sheet 2
Next R
End With
End Sub
Private Function DateColumn(Ws As Worksheet, _
ByVal SearchRow As Long) As Long
' returns the row that has today's date
' return 0 if not found
Dim SearchMe As Variant
Dim TgtDate As String
Dim Fnd As Range
If SearchRow < 1 Then Exit Function
Do
TgtDate = InputBox("Enter the target date", _
"List shift workers", _
Format(Date, "Short Date"))
' you can also set the default like Format(Date + 1, "d/m/yy")
' the sample date format must match your regional settings
If IsDate(TgtDate) Then
SearchMe = CDate(TgtDate)
' SearchMe will have the date in the format set
' in your regional settings
Exit Do
Else
MsgBox "Please enter a valid date" & vbCr & _
"in d-m-yy format", vbExclamation, "Invalid date"
' adjust the required format to your regional settings
End If
Loop While Len(TgtDate) ' enter blank or press Cancel to exit
Set Fnd = Ws.Rows(SearchRow).Find(What:=SearchMe, _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not Fnd Is Nothing Then DateColumn = Fnd.Column
End Function

VBA - get cell value, see if present in column of another workbook

I have many documents in a folder and a similar but different list in an Excel file. The documents in the folder are not always name correctly, but the value in one of the cells has the accurate name.
END GOAL: what I want to do is have code that runs through that folder, opens each file, looks at the file name in a cell*(code for that part below)* and compare it to Column A in the other Excel file, ACTIVE_FILES.xls. If it is in the list, it will move on to the next file. If it is not in the list, it will delete that file from the folder.
I already have working code which loops though a folder to open files and output information from them. I just do not know how to do a comparisson to a separate Excel worksheet or how to delete a file from a folder if it is not present.
CURRENT CODE:
This is how my current code starts out with looping through the folder (hard coded into MyFolder) to open files:
Option Explicit
Sub Active()
Sub LoopThroughDirectory()
Dim objFSO As Object, objFolder As Object, objFile As Object, dict As Object
Dim MyFolder As String
Dim StartSht As Worksheet, ws As Worksheet
Dim WB As Workbook
Dim i As Integer
Dim hc As Range, hc1 As Range, hc2 As Range, hc3 As Range, hc4 As Range, d As Range, TDS As Range
Set StartSht = Workbooks("masterfile.xlsm").Sheets("Sheet1")
'turn screen updating off - makes program faster
Application.ScreenUpdating = False
'location of the folder in which the desired TDS files are
MyFolder = "C:\Users\trembos\Documents\TDS2\progress\"
'find the header
Set hc4 = HeaderCell(StartSht.Range("A1"), "TOOLING DATA SHEET (TDS):")
'create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'get the folder object
Set objFolder = objFSO.GetFolder(MyFolder)
i = 2
'loop through directory file and print names
'(1)
'code for every excel file in the specified folder
For Each objFile In objFolder.Files
If LCase(Right(objFile.Name, 3)) = "xls" Or LCase(Left(Right(objFile.Name, 4), 3)) = "xls" Then
'(2)
'Open folder and file name, do not update links
Set WB = Workbooks.Open(Filename:=MyFolder & objFile.Name, UpdateLinks:=0)
Set ws = WB.ActiveSheet
Then, this is how I grab the cell value which contains the file name I am looking for
(searches for header "TOOLING DATA SHEET (TDS):" and then grabs the value of the cell to the right of that header cell. In my previous code, it would then print it to the next available row in column C which is no longer needed but I kept in to show my GetLastRowInColumn function which could help search through column A in the plan I want to execute)
With ws
'Print TDS name by searching for header
If Not ws.Range("A1:K1").Find(What:="TOOLING DATA SHEET (TDS):", LookAt:=xlWhole, LookIn:=xlValues) Is Nothing Then
Set TDS = ws.Range("A1:K1").Find(What:="TOOLING DATA SHEET (TDS):", LookAt:=xlWhole, LookIn:=xlValues).Offset(, 1)
StartSht.Range(StartSht.Cells(i, 1), StartSht.Cells(GetLastRowInColumn(StartSht, "C"), 1)) = TDS
Else
End If
i = GetLastRowInSheet(StartSht) + 1
End With
And finally, here are my functions which help make it all possible. Numbers designate a new function and there is an explanation next to each one.
'(8)
'Get the Values from columns with specified headers
Function GetValues(ch As Range, Optional vSplit As Variant) As Scripting.Dictionary
Dim dict As Scripting.Dictionary
Dim dataRange As Range, cell As Range
Dim theValue As String
Dim splitValues As Variant
Dim counter As Long
Set dict = New Scripting.Dictionary
Set dataRange = ch.Parent.Range(ch, ch.Parent.Cells(Rows.count, ch.Column).End(xlUp)).Cells
' If there are no values in this column then return an empty dictionary
' If there are no values in this column, the dataRange will start at the row
' *above* ch and end at ch
If (dataRange.Row = (ch.Row - 1)) And (dataRange.Rows.count = 2) And (Trim(ch.Value) = "") Then
GoTo Exit_Function
End If
For Each cell In dataRange.Cells
counter = counter + 1
theValue = Trim(cell.Value)
If Len(theValue) = 0 Then
theValue = " "
End If
'exclude any info after ";"
If Not IsMissing(vSplit) Then
splitValues = Split(theValue, ";")
theValue = splitValues(0)
End If
'exclude any info after ","
If Not IsMissing(vSplit) Then
splitValues = Split(theValue, ",")
theValue = splitValues(0)
End If
If Not dict.exists(theValue) Then
dict.Add counter, theValue
End If
Next cell
Exit_Function:
Set GetValues = dict
End Function
'(9)
'find a header on a row: returns Nothing if not found
Function HeaderCell(rng As Range, sHeader As String) As Range
Dim rv As Range, c As Range
For Each c In rng.Parent.Range(rng, rng.Parent.Cells(rng.Row, Columns.count).End(xlToLeft)).Cells
'copy cell value if it contains some string "holder" or "cutting tool"
If Trim(c.Value) = sHeader Then
'If InStr(c.Value, sHeader) <> 0 Then
Set rv = c
Exit For
End If
Next c
Set HeaderCell = rv
End Function
'(10)
'gets the last row in designated column
Function GetLastRowInColumn(theWorksheet As Worksheet, col As String)
With theWorksheet
GetLastRowInColumn = .Range(col & .Rows.count).End(xlUp).Row
End With
End Function
'(11)
'gets the last row in designated sheet
Function GetLastRowInSheet(theWorksheet As Worksheet)
Dim ret
With theWorksheet
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
ret = .Cells.Find(What:="*", _
After:=.Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
ret = 1
End If
End With
GetLastRowInSheet = ret
End Function
EDIT TO SHOW NEW WORK
POTENTIAL CODE 1: moving unwanted files to another folder - not working, basic outline because I do not know how to compare what I stated above to test a run
Option Explicit
' 33333
Sub Activate()
Dim objFSO As Object, objFolder As Object, objFile As Object, dict As Object
Dim MyFolder As String
Dim StartSht As Worksheet, ws As Worksheet
Dim WB As Workbook, wbkA As Workbook
Dim row As Long, col As Long
Dim LastRow As Long
Dim TDS1 As Object
Dim i As Integer
Dim hc As Range, hc1 As Range, hc2 As Range, hc3 As Range, hc4 As Range, d As Range, TDS As Range
Set StartSht = Workbooks("Active.xlsm").Sheets("Sheet1")
'turn screen updating off - makes program faster
' Application.ScreenUpdating = False
'location of the folder in which the desired TDS files are
MyFolder = "C:\Users\trembos\Documents\TDS2\progress_test\"
'find the headers on the sheet
Set hc4 = HeaderCell(StartSht.Range("A1"), "TOOLING DATA SHEET (TDS):")
'create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'get the folder object
Set objFolder = objFSO.GetFolder(MyFolder)
i = 2
'loop through directory file and print names
'(1)
'code for every excel file in the specified folder
For Each objFile In objFolder.Files
If LCase(Right(objFile.Name, 3)) = "xls" Or LCase(Left(Right(objFile.Name, 4), 3)) = "xls" Then
Set wbkA = Workbooks.Open(FileName:="C:\Users\trembos\Documents\TDS2\TDS_ACTIVE_FILES.xls")
For row = 1 To LastRow
With WB
If wbkA.Cells(row, 1).Value <> GetFilenameWithoutExtension(objFile.Name) Then
ElseIf row = LastRow And wbkA.Cells(row, col) <> TDS.Value Then
StartSht.Cells(i, 1) = GetFilenameWithoutExtension(objFile.Name)
i = GetLastRowInSheet(StartSht) + 1
End If
End With
Next
End If
Next
You can set the workbook ACTIVE_FILES is in as a workbook object. So perhaps you call it WBREF, and also name the worksheet ACTIVE_FILES as a worksheet object, like WSREF.
Then you can code something like:
For row = 1 to LastRow
IF WBREF.WSREF.Cells(row, *# of column in which your data is*). Value = TDS.Value Then
* close file*
Exit For
ElseIf row = LastRow And WBREF.WSREF.Cells(row,col) <> TDS.Value THEN
code how to delete file
End If
Next row
Edit: Let me explain what this code does:
For all rows in column 1 (you should code that LastRow, just search for it on this site and you will find how to do that) it checks if the content of the cell matches the value of TDS. If it finds a match it closes the file and stops looking. If the first row is not a match, it moves to the second row etc. etc. If it arrives at the last row (this is the part of code after ElseIf) and this row is also not a match you code here how to delete the file.
So you would need to place this loop of code within the loop that you have that extracts the TDS, right after that it needs to run this, before it moves on to the next TDS.
Your question is a bit long, but I think you might use the function GetInfoFromClosedFile() described here on SO.

Copy parts of row if match across workbooks

I'm having two files to work with. One is the file to be updated, the other one contains new data.
Both files contain material numbers and information about this material (quantities etc), so i want all of the fresh values (which are part of the same row) to be copied into the right place of the row in the other file.
I managed to get everything working except a VLOOKUP (which should be needed..).
Anyone? :)
'################################################################################################
'################################################################################################
'######### fnopen(): Opens a FileDialog, allowing the user to choose the GLA File. #########
'######### Returns directory/filename of selected as String #########
'################################################################################################
'################################################################################################
Function fnopen() As String
Dim strFileToOpen As String
strFileToOpen = Application.GetOpenFilename _
(Title:="Please choose GLA501 to open")
'# Change name of FileDialog (is being displayed)
Workbooks.Open filename:=strFileToOpen
'# Open Workbook
fnopen = strFileToOpen
MsgBox fnopen & " 1"
MsgBox strFileToOpen & " 2"
End Function
'################################################################################################
'################################################################################################
'################################################################################################
'######### MakeRow(): Creates String of Cell out of row and String #########
'################################################################################################
Function MakeRow(rowno As Integer, col As String) As String
MakeRow = col & CStr(rowno)
End Function
'################################################################################################
'######### getmat(): Requires row no. and returns material no. #########
'################################################################################################
Function getmat(rowno As Integer, col As String) As String
getmat = Range(MakeRow(rowno, col)).Value
End Function
'################################################################################################
'################################################################################################
Function fcat(gla_path As String, gla_name As String, lastrow As Integer) As Integer
Dim srchRange As Range, found_in_location As Range, lookFor As Range
Dim rowno As Integer, counter As Integer
Dim col As String
rowno = 16
col = "F"
counter = 0
Dim book1 As Workbook
Dim book2 As Workbook
Set book1 = ThisWorkbook
Set book2 = Workbooks(gla_name)
Set lookFor = book2.Sheets(1).Cells(rowno, 6) ' value to find
Set srchRange = book1.Sheets(2).Range(MakeRow(rowno, col), MakeRow(lastrow, col)) 'source
Set found_in_location = srchRange.Columns(1).Find(What:=lookFor, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If Not found_in_location Is Nothing Then
found_in_location.Offset(0, 85).Value = lookFor.Offset(0, 79)
Else
counter = counter + 1
End If
fcat = counter
End Function
'################################################################################################
'################################################################################################
Sub annualazy()
Dim gla_path As String, gla_name As String, col As String, rowno As Integer, counter As Integer, lastrow As Integer
MsgBox ("This VBA updates 'DC_Annual_Planning' by copying values from '4510_GLA501_DC'. Make sure to select the correct files!")
gla_path = fnopen()
gla_name = Right(gla_path, Len(gla_path) - InStrRev(gla_path, "\"))
rowno = 16
col = "F"
lastrow = Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).row
MsgBox lastrow
MsgBox gla_name
MsgBox getmat(rowno, col)
MsgBox fcat(gla_path, gla_name, lastrow)
End Sub
Could it be that the value you search for simply isn't found, and that is what creates your problem?
Anyway, when working in VBA, I prefer using Find over VLOOKUP. Without reviewing the rest of your code, and assuming the line you have problems with is lookFor.Offset(0, 79).Value = Application.VLookup(lookFor, srchRange, 2, False), you can replace it with something like:
Dim found_in_location As Range
Set found_in_location = srchRange.Columns(1).Find(What:=lookFor, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If Not found_in_location Is Nothing Then
lookFor.Offset(0, 79).Value = found_in_location.Offset(0, 2)
Else
' What will you do if the value is not found?
End If
While it takes up a bit more space than the VLOOKUP-line, it is both easier to read and to debug.

Replace a string in Column C based on matching index in Column A

I would appreciate any help on this matter. I am trying to create an Excel 2010 macro in VBA that will read strings in one spreadsheet row by row, and then search another spreadsheet to see if the value exists in a column of strings.
If/When it finds a matching string in column A, I would like to compare the string in column C of the original spreadsheet with the string in Column C of the spreadsheet being searched. If both strings are the same, I would like to move on back to the column A search and continue.
If the strings are different I would like to overwrite the string in Column C of the spreadsheet being searched. I would also like to highlight this change on the searched spreadsheet.
If no matching string is found in column A of the search spreadsheet, then I want to copy the row of the original spreadsheet into the searched spreadsheet and highlight it.
Here's what I have so far, but I can't seem to get it to work properly:
Sub SearchRows()
Dim bottomA1 As Integer
bottomA1 = Sheets("Original Spreadsheet").Range("A" & Rows.Count).End(xlUp).Row
Dim bottomA2 As Integer
bottomA2 = Sheets("Searched Spreadsheet").Range("A" & Rows.Count).End(xlUp).Row
Dim rng1 As Range
Dim rng2 As Range
Dim x As Long
Dim y As Long
Dim foundColumnA As Range
Dim foundColumnC As Range
For Each rng1 In Sheets("Original Spreadsheet").Range("A2:A" & bottomA1)
With Sheets("Searched Spreadsheet").Range("A2:A" & bottomA2)
Set foundColumnA = .Find(what:=rng1, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
For Each rng2 In Sheets("Original Spreadsheet").Range("E2:E" & bottomA1)
With Sheets("Searched Spreadsheet").Range("E2:E" & bottomA2)
Set foundSize = .Find(what:=rng2, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=True)
If foundColumnC Is Nothing Then
bottomE2 = Sheets("Column C Changes").Range("E" & Rows.Count).End(xlUp).Row
y = bottomA2 + 1
rng2.EntireRow.Copy Sheets("Column C Changes").Cells(y, "A")
Sheets("Column C Changes").Cells (y, "A").EntireRow.Interior.ColorIndex = 4
End If
End With
Next rng2
If foundTag Is Nothing Then
bottomA2 = Sheets("Column A Changes").Range("A" & Rows.Count).End(xlUp).Row
x = bottomA2 + 1
rng1.EntireRow.Copy Sheets("Column A Changes").Cells(x, "A")
Sheets("Column A Changes").Cells(x, "A").EntireRow.Interior.ColorIndex = 3
End If
End With
Next rng1
End Sub
You actually have too much code, but they're not set up cleanly. Qualify a lot of things as much as possible so it's cleaner, and try to be consistent with your style. This way you can identify the error as much as possible.
Anyway, on to the code. The basic logic you want is as follows, based on the details above:
Check if a string in Sheet1!A is in Sheet2!A.
If found, compare Column C values.
If Column C values are different, set value of Sheet2 to that in Sheet1 and highlight.
Else, exit.
If not found, copy whole row to Sheet2 and highlight.
Now that we have that written down, it's simpler! :)
Please check my screenshots for my set-up:
SCREENSHOTS:
Sheet1:
Sheet2:
Note that for Sheet2, I don't have BK207 onwards. ;) Now, onto the code.
CODE:
Sub LoopMatchReplace()
Dim ShSrc As Worksheet, ShTar As Worksheet
Dim SrcLRow As Long, TarLRow As Long, NextEmptyRow As Long
Dim RefList As Range, TarList As Range, RefCell As Range, RefColC
Dim TarCell As Range, TarColC As Range
Dim IsFound As Boolean
Dim ToFind As String
With ThisWorkbook
Set ShSrc = .Sheets("Sheet1")
Set ShTar = .Sheets("Sheet2")
End With
'Get the last rows for each sheet.
SrcLRow = ShSrc.Range("A" & Rows.Count).End(xlUp).Row
TarLRow = ShTar.Range("A" & Rows.Count).End(xlUp).Row
'Set the lists to compare.
Set RefList = ShSrc.Range("A2:A" & SrcLRow)
Set TarList = ShTar.Range("A2:A" & TarLRow)
'Initialize boolean, just for kicks.
IsFound = False
'Speed up the process.
Application.ScreenUpdating = False
'Create the loop.
For Each RefCell In RefList
ToFind = RefCell.Value
'Look for the value in our target column.
On Error Resume Next
Set TarCell = TarList.Find(ToFind)
If Not TarCell Is Nothing Then IsFound = True
On Error GoTo 0
'If value exists in target column...
If IsFound Then
'Compare the Column C of both sheets.
Set TarColC = TarCell.Offset(0, 2)
Set RefColC = RefCell.Offset(0, 2)
'If they are different, set the value to match and highlight.
If TarColC.Value <> RefColC.Value Then
TarColC.Value = RefColC.Value
TarColC.Interior.ColorIndex = 4
End If
Else 'If value does not exist...
'Get next empty row, copy the whole row from source sheet, and highlight.
NextEmptyRow = ShTar.Range("A" & Rows.Count).End(xlUp).Row + 1
RefCell.EntireRow.Copy ShTar.Rows(NextEmptyRow)
ShTar.Rows(NextEmptyRow).SpecialCells(xlCellTypeConstants).Interior.ColorIndex = 3
End If
'Set boolean check to False.
IsFound = False
Next RefCell
Application.ScreenUpdating = True
End Sub
Kindly read the comments for the codeblocks so you get an understanding of what I'm doing. Also, note the way that I have qualified everything and properly set them up in a very clean way. Clean code is 50% good code.
Check the following screenshot to see the results after running the code.
END RESULT:
Note the added rows at the end and the changed values in Column C. I did not have the whole row highlighted as I believe that's bad practice and messy, but it's up to you to change the respective lines and values to suit your taste for the end result.
Let us know if this helps.
I think you can use this code.
Values not found will be added to the end of destination sheet.
Differences are signed with a blue(change if you want) background color.
Sub copy_d()
Dim r1 As Long, rfound, vfound
Dim w1, w2, v, lastR As Long, lastC As Long
Set w1 = Sheets("sheet1") ' change the origin sheet at will
Set w2 = Sheets("sheet2") ' change the destination sheet at will
r1 = 1 ' assuming data start in row 1, change it if not
Do While Not IsEmpty(w1.Cells(r1, 1))
v = w1.Cells(r1, 1)
rfound = Application.Match(v, w2.Columns(1), 0) ' look for value
If Not IsError(rfound) Then ' found it?
vfound = w2.Cells(rfound, 3)
If w1.Cells(r1, 3) <> vfound Then ' value in column C is different?
w2.Cells(rfound, 3) = w1.Cells(r1, 3) ' update based on origin sheet
lastC = w2.Cells(rfound, 1).End(xlToRight).Column
w2.Range(w2.Cells(rfound, 1), w2.Cells(rfound, lastC)).Interior.ColorIndex = 5
End If
Else
lastR = w2.Cells(1, 1).End(xlDown).Row + 1
w1.Rows(r1).copy Destination:=w2.Rows(lastR) ' copy to last row of dest sheet
lastC = w2.Cells(lastR, 1).End(xlToRight).Column
w2.Range(w2.Cells(lastR, 1), w2.Cells(lastR, lastC)).Interior.ColorIndex = 5
End If
r1 = r1 + 1
Loop
End Sub