Spreadsheet Update from external excel VBA - vba

I have been working on this code for sometime, taking what I can from other posts and learning as I go. I am new to VBA. I am trying to have a master spreadsheet update from other excel sheets. I have wrote a code to check the value of column C and if it has a value in the Master that is not in the other to highlight the row red. IF the other sheet has a value that the master does not it, inserts the entire row and highlights green. The part that I can not seem to get working is how to update the existing rows with new information when the value of column C is a match. Everytime I try, it messes everything up.
Here is my code:
Sub FindDifferences()
Application.ScreenUpdating = False
Dim cell As Range
Dim cel1 As Range
Dim cel2 As Range
Dim wkb1 As Workbook
Dim wkb2 As Workbook
Dim wks1 As Worksheet
Dim wks2 As Worksheet
Dim lRow As Long
Dim iCntr As Long
Dim r1 As Range
Dim r2 As Range
Dim i As Integer
Dim j As Integer
Dim lastRow1 As Integer
Dim lastRow2 As Integer
Dim lastRow As Long
Dim recRow As Long
Dim p As Long
Dim fCell As Range
Set wkb1 = Workbooks.Open(Filename:="C:\Users\James.R.Dickerson\...\09-24-2018-2.xlsx.xlsm")
Set wks1 = wkb1.Worksheets("Job List")
Set wkb2 = ThisWorkbook
Set wks2 = wkb2.Worksheets("Code 200 TECH ASSISTs")
lRow = 200
recRow = 1
For iCntr = lRow To 1 Step -1
If Cells(iCntr, 1).Interior.Color = RGB(156, 0, 6) Then
Rows(iCntr).Delete
End If
Next
With wks1
Set r1 = .Range("C2", .Cells(.Rows.Count, .Columns("C:C").Column).End(xlUp))
End With
With wks2
Set r2 = .Range("C2", .Cells(.Rows.Count, .Columns("C:C").Column).End(xlUp))
End With
lastRow1 = wks2.UsedRange.Rows.Count
lastRow2 = wks1.UsedRange.Rows.Count
For i = 1 To lastRow1
For j = 1 To lastRow2
If r2(i).Value <> "" Then 'This will omit blank cells at the end (in the event that the column lengths are not equal.
If r1(j).Value = r2(i).Value Then
r2(i).EntireRow.Delete
r1(j).EntireRow.Copy
r2(i).EntireRow.Insert
r2(i).EntireRow.Interior.Color = RGB(255, 255, 255) 'White background
r2(i).EntireRow.Font.Color = RGB(0, 0, 0) 'Black font color
Application.CutCopyMode = False
Exit For
Else
If InStr(1, r1(j).Value, r2(i).Value, vbTextCompare) > 0 Then
'You may notice in the above instr statement, I have used vbTextCompare instead of its numerical value, _
I find this much more reliable.
r2(i).EntireRow.Interior.Color = RGB(255, 255, 255) 'White background
r2(i).EntireRow.Font.Color = RGB(0, 0, 0) 'Black font color
Exit For
Else
r2(i).EntireRow.Interior.Color = RGB(156, 0, 6) 'Dark red background
r2(i).EntireRow.Font.Color = RGB(255, 199, 206) 'Light red font color
End If
End If
End If
Next j
Next i
With wks1
lastRow = .Cells(.Rows.Count, "C").End(xlUp).Row
For i = 2 To lastRow
'See if item is in Master sheet
Set fCell = wks2.Range("C:C").Find(what:=.Cells(i, "C").Value, lookat:=xlWhole, MatchCase:=False)
If Not fCell Is Nothing Then
'Record is already in master sheet
recRow = fCell.Row
Else
'Need to move this to master sheet after last found record
.Cells(i, "C").EntireRow.Copy
wks2.Cells(recRow + 1, "C").EntireRow.Insert
wks2.Cells(recRow + 1, "C").EntireRow.Interior.Color = RGB(0, 190, 8)
recRow = recRow + 1
End If
Next i
End With
Application.CutCopyMode = False
wkb1.Close
Application.ScreenUpdating = True
'ActiveWorkbook.Save
End Sub
Update is the code above works fine, it just skips a few rows and I can not figure out why. Any assistance is appreciated. Thank you.

This block:
.Cells(p, "C").EntireRow.Copy
wks2.Cells(p, "C").EntireRow.Delete
wks2.Cells(recRow1 + 1, "C").EntireRow.Insert
is in wrong order because .Delete empties copy buffer so you insert an empty row. Change order of commands this way:
wks2.Cells(p, "C").EntireRow.Delete
.Cells(p, "C").EntireRow.Copy
wks2.Cells(recRow1 + 1, "C").EntireRow.Insert
and it will be better :)

Related

Excel VBA: Locking range of cells

Im trying to lock a few ranges of cells to prevent them from being altered outside of the button press.
I have the following code so far:
Private Sub DateRangePayer()
Dim unionRange As Range, uRng As Range, EssentialWrite As Range, chCell As Range, chRng As Range
Dim d As Long, k As Long, x As Long
ActiveSheet.Unprotect
Set EssentialWrite = Sheets("Essential Info").Range("E2:E6")
Set unionRange = ActiveSheet.Range("Q8:R12, T8:T12, Q16:R20, T16:T20")
Set chRng = ActiveSheet.Range("Q8:R12, T8:T12, Q16:R20, T16:T20")
x = Sheets("Essential Info").Range("G19").Value
ReDim OArr(1 To 5, 1 To 1) As Variant
For d = DateSerial(Year(x), Month(x), 1) To DateSerial(Year(x), Month(x) + 1, 0) - 1
If Weekday(d, vbSunday) = 7 Then
k = k + 1
OArr(k, 1) = d
End If
Next d
If k = 4 Then OArr(k + 1, 1) = "-"
For Each uRng In unionRange.Areas
uRng.Value = OArr
uRng.NumberFormat = "dd-mmmm"
Next uRng
For Each chCell In chRng.Cells
chCell.MergeArea.Locked = (chCell.Value <> "")
Next chCell
EssentialWrite.Value = OArr
EssentialWrite.NumberFormat = "dd-mmmm"
ActiveSheet.Protect
End Sub
The main parts of the code are the
ActiveSheet.Unprotect
For Each chCell In chRng.Cells
chCell.MergeArea.Locked = (chCell.Value <> "") Next chCell
ActiveSheet.Protect
Currently the code executes with zero errors. However the range of cells is not locked at all and is actually editable in its entirety.
Im doing this to prevent unexpected user entries in the specified cells
Any advice on what may work.
Im sorry if the code is a little messy. Im kinda just hacking together at this point and relatively new to this
This code locks only the code that say LOCKED in the image below.
Sub lockCells()
Dim ws As Worksheet
Set ws = Sheets("Sheet1")
ws.Cells.Locked = False
Dim rng As Range
Set rng = ws.Range("A1:A10")
Dim cell As Range
For Each cell In rng
cell.Locked = cell.Value <> ""
Next cell
ws.Protect 1234
End Sub
I'm not sure this is 'best practice' but I'd use:
chCell.Cells(1, 1).Locked = (chCell.Value <> "")

hide column based on font color in a table vba

I have some data in sheet1 as a table (named Table1), and I am changing the font color for some headers based on name and I want to only hide the header if its font color is black so keep orange and white un-hide. When I open the original worksheet, column headers has font color of white.
Right now when I run my codes, there are no error, but I only see columns with headers of orange font color which is not correct. For some reason when I convert my data into range, it works but I don't want to use unlist and re-create a table for the data.
Sub Data_Formatting()
Dim i, j, k As Long
Range(Range("A1"), Range("A1").End(xlToRight)).Interior.Color = RGB(79, 129, 189)
Last = Cells(1, Columns.Count).End(xlToLeft).Column
For i = Last To 1 Step -1
If (Cells(1, i).Value) = "System" Then
Cells(1, i).Font.Color = RGB(0, 0, 0)
End If
Next i
For j = Last To 1 Step -1
If (Cells(1, j).Value) = "AOB" Then
Cells(1, j).Font.Color = RGB(255, 153, 0)
End If
Next j
Range("A:D").Columns.AutoFit
Dim l As Long
Dim lColumn As Long
Dim ws As Worksheet: Set ws = ActiveSheet
'Last column
lColumn = ws.Cells(1, Columns.Count).End(xlToLeft).Column
For l = 1 To lColumn
If Cells(1, l).Font.Color = RGB(0, 0, 0) Then
Cells(1, l).EntireColumn.Hidden = True
Else
Cells(1, l).EntireColumn.Hidden = False
End If
Next
End Sub
You only need to loop once here and do all of your logic in that one loop. The way you are doing it now is looping three times over the same set of columns just to perform slightly different actions.
Sub Data_Formatting()
Dim i as Long
'set the background to blue
Range(Range("A1"), Range("A1").End(xlToRight)).Interior.Color = RGB(79, 129, 189)
'Find last cell
Last = Cells(1, Columns.Count).End(xlToLeft).Column
'autofit before hiding
Range("A:D").Columns.AutoFit
'loop once
For i = Last To 1 Step -1
If (Cells(1, i).Value) = "System" Then
Cells(1, i).Font.Color = RGB(0, 0, 0) 'black
Columns(i).Hidden = True
ElseIf Cells(1, j).Value = "AOB" Then
Cells(1, j).Font.Color = RGB(255, 153, 0) 'orange
Columns(i).Hidden = False
End If
Next i
End Sub
With this change we don't have to bother detecting the cell color since you are setting that based on the value in the same loop. Test the value, set the color, and hide it all in one shot.

Is there any fast way to copy Duplicate rows(next to each other) from a Sheet to another by analyzing multiple columns in Excel VBA?

I want to copy duplicate rows from a sheet to another by analyzing multiple columns in excel, I can do it by applying Nested For loops to compare multiple columns but number of rows in my sheet is around 6000. So if I apply nested For loop to compare rows by analyzing 2 columns it requires around 17991001 iterations, which slows down my System. Is there any fast way to do that???
my Function is
Sub findDuplicates(ByVal sheet As Worksheet, name As String, ByRef row As Integer, ByVal Sheet2 As Worksheet)
Dim i As Integer
Dim numRow As Integer
'Dim matchFound As Long
'Dim myRange1 As Range
'Dim myRange2 As Range
numRow = sheet.Range("J2", sheet.Range("J2").End(xlDown)).Rows.Count
With Sheet2
Range(Cells(row, "A"), Cells(row, "N")).MergeCells = True
With Cells(row, "A")
.Font.name = "Bell MT"
.Font.FontStyle = "Bold Italic"
.Font.Size = 20
.Font.Color = RGB(255, 99, 71)
.Value = "Multiple Forms Found in " & name & " for single household"
End With
row = row + 1
End With
For i = 1 To numRow + 1
'matchFound
'If i <> matchFound Then
sheet.Rows(i).Copy Sheet2.Rows(row)
row = row + 1
'sheet.Rows(matchFound).Copy Sheet2.Rows(row)
'row = row + 1
'End If
Next i
End Sub
Note - I added some comments to make you understand what I want to do.
The Summery of my function is to take two sheets and check the J and K columns of sheet 1, If two rows found same J and K column's value then both rows are copied to sheet2 (next to each other)
Try this. Modified from Siddharth Rout's answer here.
Private Sub CommandButton2_Click()
Dim col As New Collection
Dim SourceSheet As Worksheet
Dim DestSheet As Worksheet
Dim i As Long
Dim lLastRow As Long
Application.ScreenUpdating = False
Set SourceSheet = ThisWorkbook.Sheets("Sheet1")
Set DestSheet = Worksheets("Sheet2")
lLastRow = SourceSheet.Cells(Rows.Count, 10).End(xlUp).row
DestSheetLastRow = 1
With SourceSheet
For i = 1 To lLastRow
On Error Resume Next
col.Add i, CStr(.Range("J" & i).Value) 'Add elements to collection
If Err.Number <> 0 Then 'If element already present
TheVal = CStr(SourceSheet.Range("J" & i).Value) 'Get the duplicate value
TheIndex = col(TheVal) 'Get the original position of duplicate value in the collection (i.e., the row)
If (.Cells(i, 11).Value = .Cells(TheIndex, 11).Value) Then 'Check the other column (K). If same value...
SourceSheet.Range(Cells(TheIndex, 1), Cells(TheIndex, 20)).Copy DestSheet.Cells(DestSheetLastRow, 1) 'Set your range according to your needs. 20 columns in this example
SourceSheet.Range(Cells(i, 1), Cells(i, 20)).Copy DestSheet.Cells(DestSheetLastRow, 21)
DestSheetLastRow = DestSheetLastRow + 1
Err.Clear
End If
End If
Next i
End With
Application.ScreenUpdating = True
End Sub
Finally, This Works for me
Sub findDuplicates(ByVal sheet As Worksheet, name As String, ByRef row As Integer, ByVal Sheet2 As Worksheet)
Dim i As Integer
Dim j As Integer
Dim numRow As Integer
Dim count As Integer
Dim myRange1 As Range
Dim myRange2 As Range
Dim myRange3 As Range
Set myRange1 = sheet.Range("J2", sheet.Range("J2").End(xlDown)).Rows
Set myRange2 = sheet.Range("K2", sheet.Range("K2").End(xlDown)).Rows
numRow = sheet.Range("J2", sheet.Range("J2").End(xlDown)).Rows.count
With Sheet2
Range(Cells(row, "A"), Cells(row, "N")).MergeCells = True
With Cells(row, "A")
.Font.name = "Bell MT"
.Font.FontStyle = "Bold Italic"
.Font.Size = 20
.Font.Color = RGB(255, 99, 71)
.Value = "Multiple Forms Found in " & name & " for single household"
End With
sheet.Rows(1).Copy .Rows(row + 1)
.Rows(row + 1).WrapText = False
row = row + 2
End With
j = row
For i = 1 To numRow + 1
count = WorksheetFunction.CountIfs(myRange1, sheet.Cells(i, "J"), myRange2, sheet.Cells(i, "K"))
If count > 1 Then
sheet.Rows(i).Copy Sheet2.Rows(row)
row = row + 1
End If
Next i
Set myRange3 = Sheet2.Range(Cells(j, 1), Cells(row - 1, 192))
With Sheet2.Sort
.SortFields.Add Key:=Range("J1"), Order:=xlAscending
.SortFields.Add Key:=Range("K1"), Order:=xlAscending
.SetRange myRange3
.Header = xlNo
.Orientation = xlTopToBottom
.Apply
End With
End Sub

Compare the value of a Cell in a Column on one sheet with all the values in a column on another sheet. Color the row depending on the result

I have a list of names on Sheet1 ColumnA and need to see if they appear in Sheet2 ColumnB.
If a name on Sheet1 ColumnA exist on Sheet2 ColumnB, I need to color the Row on Sheet1.ColumnA Green. If not, color the row Red.
The code that ended up working for my specific issue was this:
Sub ColorCells()
Application.ScreenUpdating = False
Dim c, Finder
With Sheets("Sheet1")
For Each c In .Range("A1:A" & .Cells(Rows.CountLarge, "A").End(xlUp).Row)
Set Finder = Sheets("Sheet2").Range("B:B").Find(c.Value, LookAt:=xlWhole)
If Not Finder Is Nothing Then
c.EntireRow.Interior.Color = RGB(180, 230, 180)
Else
c.EntireRow.Interior.Color = RGB(230, 180, 180)
End If
Next c
End With
Application.ScreenUpdating = True
End Sub
I came up with this, hopefully ,it is what You're looking for.
Dim rcnt As Long, ws1 As Worksheet, ws2 As Worksheet
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
rcnt = ws2.Range("B1", ws2.Range("B2").End(xlDown)).Rows.Count
For x = 1 To rcnt
If ws1.Cells(x, 1) = ws2.Cells(x, 2) Then
ws1.Cells(x, 1).EntireRow.Interior.Color = RGB(0, 255, 0)
Else:
ws1.Cells(x, 1).EntireRow.Interior.Color = RGB(255, 0, 0)
End If
Next x
Basic solution - this may take some time if you have incredibly large data sets.
You can change LookAt:=xlWhole to LookAt:=xlPart if you want to see if the name exists in part of the cell instead of an exact match.
Sub ColorCells()
Application.ScreenUpdating = False
Dim c, Finder
With Sheets("Sheet1")
For Each c In .Range("A1:A" & .Cells(Rows.CountLarge, "A").End(xlUp).Row)
Set Finder = Sheets("Sheet2").Range("B:B").Find(c.Value, LookAt:=xlWhole)
If Not Finder Is Nothing Then
c.Interior.Color = RGB(180, 230, 180)
Else
c.Interior.Color = RGB(230, 180, 180)
End If
Next c
End With
Application.ScreenUpdating = True
End Sub
Another way you can do this using conditional formatting
The formulas are:
=COUNTIFS(Sheet2!$B$1:$B$500000,A1)=0
=COUNTIFS(Sheet2!$B$1:$B$500000,A1)>0
You can change the ranges in the formula to the ranges you wish to use
You can add this to the first cell, click the Format Painter - Press F5 - and put in the range you would like it to apply to.
Finally, if your values are unique, you can use this method:
This should be a very fast method using dictionaries and variant arrays - all the comparisons are done in memory.
Sub ColorTheCells()
Dim s1, s2, r1(), r2(), d1, x
Set d1 = CreateObject("Scripting.Dictionary")
Set s1 = Sheets("Sheet1")
Set s2 = Sheets("Sheet2")
r1 = s1.Range("A1:A" & s1.Cells(Rows.CountLarge, "A").End(xlUp).Row).Value
r2 = s2.Range("B1:B" & s2.Cells(Rows.CountLarge, "B").End(xlUp).Row).Value
For x = LBound(r1, 1) To UBound(r1, 1)
d1.Add r1(x, 1), x
Next x
s1.Range("A1:A" & s1.Cells(Rows.CountLarge, "A").End(xlUp).Row).Interior.Color = RGB(230, 180, 180)
For x = LBound(r2, 1) To UBound(r2, 1)
If d1.Exists(r2(x, 1)) Then s1.Cells(d1(r2(x, 1)), s1.Cells(1, 1).Column).Interior.Color = RGB(180, 230, 180)
Next x
End Sub

looping through an entire column of values and if value matches, cut and paste it to another sheet

I have columns A, B, C, D, and E with data.
My goal is to start in cell A1, loop through every single record in column A while looking for a particular value "Grey". If the text in cells is equal to "Grey" then i want to cut and paste then entire row to a newly created sheet, starting in A1. here's what my code looks like ....
Dim n As Long
Dim nLastRow As Long
Dim nFirstRow As Long
Dim lastRow As Integer
ActiveSheet.UsedRange
Set r = ActiveSheet.UsedRange
nLastRow = r.Rows.Count + r.Row - 1
nFirstRow = r.Row
Worksheets("Original").Activate
With Application
.ScreenUpdating = False
Sheets.Add.Name = "NewSheet"
Sheets("Original").Select
Range("A1").Select
Set r = ActiveSheet.UsedRange
nLastRow = r.Rows.Count + r.Row - 1
nFirstRow = r.Row
With ActiveSheet
For n = nLastRow To nFirstRow Step -1
If .Cells(n, "A") = "Grey" Then
.Cells(n, "A").EntireRow.Cut Sheets("NewSheet").Cells(i, "A")
.Cells(n, "A").EntireRow.Delete
n = n + 1
End If
Next
End With
.ScreenUpdating = True
End With
So this macro creates a new sheet - however when it gets to a cell where the value is grey it gives me an error on this line....
.Cells(n, "A").EntireRow.Cut Sheets("NewSheet").Cells(i, "A")
Error says:
Application defined or object defined error.
Anyone have any idea why?
You need to declare i, and set it. As mentioned, the first time it occurs it's looking to paste in row 0, which doesn't exist.
Also, it's best to avoid using .Select/.Activate, and work directly with the data.
How does this work?
Sub t()
Dim r As Range
Dim n As Long, i As Long, nLastRow As Long, nFirstRow As Long
Dim lastRow As Integer
Dim origWS As Worksheet, newWS As Worksheet
Set origWS = Worksheets("Original")
Set newWS = Sheets.Add
newWS.Name = "NewSheet"
Set r = origWS.UsedRange
nLastRow = r.Rows.Count + r.Row - 1
nFirstRow = r.Row
i = 1
With Application
.ScreenUpdating = False
With origWS
For n = nLastRow To nFirstRow Step -1
If .Cells(n, "A") = "Grey" Then
.Cells(n, "A").EntireRow.Copy newWS.Cells(i, "A")
.Cells(n, "A").EntireRow.Delete
i = i + 1
End If
Next
End With
.ScreenUpdating = True
End With
End Sub
You also don't need to do n = n + 1 (unless I missed something).
Edit: Changed .Cut to .Copy, per OP's wish to keep formatting.
Or you may try something like this...
Sub CopyToNewSheet()
Dim sws As Worksheet, dws As Worksheet
Application.ScreenUpdating = False
Set sws = Sheets("Original")
On Error Resume Next
Set dws = Sheets("NewSheet")
dws.Cells.Clear
On Error GoTo 0
If dws Is Nothing Then
Sheets.Add(after:=sws).Name = "NewSheet"
Set dws = ActiveSheet
End If
sws.Rows(1).Insert
On Error Resume Next
With sws.Range("A1").CurrentRegion
.AutoFilter field:=1, Criteria1:="Grey"
.SpecialCells(xlCellTypeVisible).Copy dws.Range("A1")
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
dws.Rows(1).Delete
Application.ScreenUpdating = True
End Sub