I have an Excel workbook that has a master sheet that keeps track of items and their current location, and another sheet that keeps track of past locations or where an item has been. Currently when a record is changed in the master sheet, the row is manually copied and pasted into the 2nd sheet. I would like to create a macro that would find items in the master sheet that are not in the 2nd sheet and copy them to the 2nd sheet when records change.
Below is a sample macro I found and modified that is close, but it copies and pastes all rows instead of the new or different ones. The rows would only need to be compared on columns A, B, and D.
Public Sub Sample()
Dim shM As Worksheet, sh2 As Worksheet
Dim shMData As Variant
Dim sh2DataA As Variant
Dim sh2Data As Variant
Dim iM As Long, os2 As Long, i2 As Variant
Dim DoSearch As Boolean
Set shM = Sheets(1)
Set sh2 = Sheets(2)
With shM
shMData = .Range(.[A1], .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 4)
End With
DoSearch = False
For iM = 2 To UBound(shMData, 1)
With sh2
sh2DataA = .Range(.[A1], .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 1)
sh2Data = .Range(.[A1], .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 4)
End With
os2 = 0
Do
If UBound(shMData, 1) > 1 Then
i2 = Application.Match(shMData(iM, 1), sh2DataA, 0)
Else
If shMData(iM, 1) = sh2DataA Then
i2 = 1
Else
i2 = CVErr(xlErrNA)
End If
End If
If Not IsError(i2) Then
If (shMData(iM, 2) = sh2Data(i2, 2)) And (shMData(iM, 4) = sh2Data(i2, 4)) Then
MsgBox "Match found Master = " & iM & ", sheet2 = " & i2 + os2
Else
shM.Activate
shM.Range(Cells(iM, 1), Cells(iM, 7)).Select
Selection.Copy
sh2.Select
FinalRow = Range("A65536").End(xlUp).Row
NextRow = Range("A65536").End(xlUp).Row + 1
Range("A" & NextRow).Select
ActiveSheet.Paste
End If
os2 = os2 + i2
If os2 < UBound(sh2Data, 1) Then
With sh2
sh2DataA = .Range(.Cells(i2 + os2, 1), .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 1)
sh2Data = .Range(.Cells(i2 + os2, 1), .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 4)
End With
DoSearch = True
Else
DoSearch = False
End If
Else
shM.Activate
shM.Range(Cells(iM, 1), Cells(iM, 7)).Select
Selection.Copy
sh2.Select
FinalRow = Range("A65536").End(xlUp).Row
NextRow = Range("A65536").End(xlUp).Row + 1
Range("A" & NextRow).Select
ActiveSheet.Paste
DoSearch = False
End If
Loop Until Not DoSearch
Next
End Sub
The message box was added only to verify that the code was working - it is not a necessary component. Thanks again for any advice you can give.
Assuming you never get two rows of the exact same thing in your master list, you can just use the built-in Excel feature Remove Duplicates (on the Data tab in 2010 at least). If you have x duplicate rows, all the same, x-1 of them are deleted. So, you could just copy the entire other table, paste it below the master list, and then run remove duplicates on the master list. All you need to know is the VBA for removing duplicates.
ActiveSheet.Range("$A$40:$D$43").RemoveDuplicates Columns:=Array(1, 3, 4), Header:=xlNo
Adjust as necessary
Thank you all for your help I found a solution however it does not work in Excel 2003. If anyone knows off the top of their head why that would be great else I think I figure it out. Here is the code.
[HTML]Public Sub NewEntWhole()
Dim loM As ListObject, lo2 As ListObject
Dim TblMData As Variant
Dim iM As Long
Dim dDate As Date
Dim strDate As String
Dim lDate As Long
Dim rng As Range
Dim ct As Variant
Dim shM As Worksheet
Dim sh2 As Worksheet
Dim hdM As Integer
hdM = 0 'rows above table M
Set shM = Sheets(1)
Set sh2 = Sheets(2)
Set loM = Sheets(1).ListObjects(1)
Set lo2 = Sheets(2).ListObjects(1)
With loM
TblMData = .DataBodyRange
End With
For iM = 2 To UBound(TblMData, 1) + 1
sh2.Activate
With lo2
.Range.AutoFilter Field:=1, Criteria1:=loM.Range(iM, 1).Value
.Range.AutoFilter Field:=2, Criteria1:=loM.Range(iM, 2).Value
If IsDate(loM.Range(iM, 4)) Then
sDate = loM.Range(iM, 4)
dDate = DateSerial(Year(sDate), Month(sDate), Day(sDate))
lDate = dDate
.Range.AutoFilter Field:=4, Criteria1:=">=" & lDate, Operator:=xlAnd, Criteria2:="<" & lDate + 1
Else
.Range.AutoFilter Field:=4, Criteria1:=loM.Range(iM, 4).Value
End If
End With
Set rng = lo2.AutoFilter.Range
ct = rng.Columns(1).SpecialCells(xlCellTypeVisible).Count - 1
If ct = 0 And loM.Range(iM, 1).Value > 0 Then
shM.Activate
shM.Range(Cells((iM + hdM), 1), Cells((iM + hdM), 7)).Copy
sh2.Activate
FinalRow = Range("B65536").End(xlUp).Row
NextRow = Range("B65536").End(xlUp).Row + 1
Range("A" & NextRow).Select
ActiveSheet.Paste
End If
With lo2
.Range.AutoFilter Field:=1
.Range.AutoFilter Field:=2
.Range.AutoFilter Field:=4
End With
Next
shM.Activate
End Sub[/HTML]
Related
I have created below code which works like IF Col"B" any cell <> "" And Col"L" any cell = "Leop" then add row below to the active cell.
I mean I'm trying to achieve is to insert single row after certain row which contain in column B any value, and if column L in same row contains value = "Leop". Then add the row after that certain row.
But an error is appear. Compile Error: Invalid use of property on xlDown
Your help will be appreciated to fix it.
From this:
to this:
Sub firstcondition()
Dim ws As Worksheet
Dim LRow As Long
Dim rng As Range
Dim rng2 As Range
Dim i As Long
Dim p As Long
Dim dat As Variant
Dim datt As Variant
Dim IRow As Long
Set ws = Thisworkbooks.Sheets("Sheet2")
With ws
LRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("B2:B" & LRow)
Set rng2 = .Range("L2:L" & LRow)
dat = rng
datt = rng2
IRow = Selection.Row
For i = LBound(dat, 1) To UBound(dat, 1)
For p = LBound(datt, 1) To UBound(datt, 1)
If dat(i, 1) <> "" And datt(p, 1) = "Leop" Then
Rows(IRow + 1).Select
Selection.Insert Shift: xlDown
End If
End Sub
It will be like in formula:
IF(AND(B2<>"",L2="Leop"),"InsertRowBelow to Row 2 If condition is met","")
and will drag it down to the lastRow.
Thisworkbooks.Sheets("Sheet2") should be Thisworkbook.Sheets("Sheet2") and missing = in Selection.Insert Shift:= xlDown
Inserting or deleting rows will change the last row number so start at the bottom and work upwards.
Option Explicit
Sub firstcondition()
Dim ws As Worksheet, LRow As Long, r As Long
Dim n As Long
Set ws = ThisWorkbook.Sheets("Sheet2")
With ws
LRow = .Range("B" & .Rows.Count).End(xlUp).Row
For r = LRow To 2 Step -1
If .Cells(r, "B") <> "" And .Cells(r, "L") = "Leop" Then
.Rows(r + 1).Insert shift:=xlDown
n = n + 1
End If
Next
End With
MsgBox n & " rows inserted", vbInformation
End Sub
Try this with autofilter, you dont have to loop through each row. So it will work faster for larger data.
Option Explicit
Sub firstcondition()
Dim ws As Worksheet
Dim LRow As Long, cl As Range
Set ws = ThisWorkbook.Sheets("Sheet2")
LRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
ws.Range("L1:L" & LRow).AutoFilter 1, "Leop"
For Each cl In ws.Range("_FilterDatabase").SpecialCells(12).Cells
If ws.Range("B" & cl.Row) <> "" Then
cl.Offset(1).EntireRow.Insert Shift:=xlDown
End If
Next
ws.AutoFilterMode = False
End Sub
I'm writing a code to look for a specific keyword ("Team") and when found I want to paste the team name in a specific column ("D") for all rows above. If the keyword is not found I want to copy the entire row. This all pasted into a new sheet.
What I have:
x-------------x------------x
x-------------x------------x
Team A----x------------x
x-------------x-------------x
x-------------x-------------x
Team B----x-------------x
What I want:
x----x----x----A
x----x----x----A
x----x----x----B
x----x----x----B
Here's what I have so far:
Sub fun()
Dim j as Integer
Dim lastrow as Integer
Dim team as String
Dim sh As Worksheet
sh = Sheets("Sheet 1")
lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlup).Row
Range("A" & lastrow).Select
for j = 1 to lastrow
If Instr(Cells(j,1).Value, "Team") Then
Cells(j,1).Value = Replace(Cells(j,1).Value, "Team ", "")
Cells(j,1).Value = team
Else
Range(Cells(j,1), Cells(j,3). Select
Selection.Copy
Windows("sheet.xlsm").Activate
ActiveSheet.Cells(1,1).Select
Selection.PasteSpecial Paste:=xlPasteValues, Transpose:=False
End If
next j
End Sub
I'm able to meet the second condition and paste entire rows but I'm unsure how to copy the team names and post them in column D in the new sheet.
Something like this:
Sub fun()
Dim j As Long, destRow As Long
Dim team As String, v, rngTeam As Range
Dim sh As Worksheet, shDest As Worksheet
Set sh = Sheets("Sheet1")
Set shDest = Sheets("Sheet2") 'for example
destRow = shDest.Cells(Rows.Count, 1).End(xlUp).Row + 1
For j = 1 To sh.Cells(Rows.Count, 1).End(xlUp).Row
v = sh.Cells(j, 1).Value
If InStr(v, "Team") > 0 Then
If Not rngTeam Is Nothing Then rngTeam.Value = Replace(v, "Team ", "") '<< set for already-copied rows
Set rngTeam = Nothing 'reset the range
Else
shDest.Cells(destRow, 1).Resize(1, 3).Value = sh.Cells(j, 1).Resize(1, 3).Value
'add to the range to populate next time we hit a "Team"
If rngTeam Is Nothing Then
Set rngTeam = shDest.Cells(destRow, 4)
Else
Set rngTeam = Application.Union(shDest.Cells(destRow, 4), rngTeam)
End If
destRow = destRow + 1
End If
Next j
End Sub
I have been trying to come up with/find a VBA code that copies blocks of data under my first block. Each block is 19 columns followed by a blank. The number of rows per block can vary.
See my screenshot below:
Therefore, I would like all my data continuous in the first columns A:S. Any help is highly appreciated.
I found the following code online, but this only pastes everything into the first column
Sub Column()
Dim iLastcol As Long
Dim iLastRow As Long
Dim jLastrow As Long
Dim ColNdx As Long
Dim ws As Worksheet
Dim myRng As Range
Dim ExcludeBlanks As Boolean
Dim mycell As Range
ExcludeBlanks = (MsgBox("Exclude Blanks", vbYesNo) = vbYes)
Set ws = ActiveSheet
iLastcol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
On Error Resume Next
Application.DisplayAlerts = False
Worksheets("Alldata").Delete
Application.DisplayAlerts = True
Sheets.Add.Name = "Alldata"
For ColNdx = 1 To iLastcol
iLastRow = ws.Cells(ws.Rows.Count, ColNdx).End(xlUp).Row
Set myRng = ws.Range(ws.Cells(1, ColNdx), _
ws.Cells(iLastRow, ColNdx))
If ExcludeBlanks Then
For Each mycell In myRng
If mycell.Value <> "" Then
jLastrow = Sheets("Alldata").Cells(Rows.Count, 1) _
.End(xlUp).Row
mycell.Copy
Sheets("Alldata").Cells(jLastrow + 1, 1) _
.PasteSpecial xlPasteValues
End If
Next mycell
Else
myRng.Copy
jLastrow = Sheets("Alldata").Cells(Rows.Count, 1) _
.End(xlUp).Row
mycell.Copy
Sheets("Alldata").Cells(jLastrow + 1, 1) _
.PasteSpecial xlPasteValues
End If
Next
Sheets("Alldata").Rows("1:1").EntireRow.Delete
ws.Activate
End Sub
Basic approach:
Sub Tester()
Dim c As Range, addr
Set c = ActiveSheet.Range("T1")
Do
Set c = c.End(xlToRight)
If c.Column = Columns.Count Then Exit Do
addr = c.Address 'strire the address since Cut will move c
c.CurrentRegion.Cut c.Parent.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
Set c = ActiveSheet.Range(addr) '<< reset c
Loop
End Sub
This is a little more basic than #TimWilliams
With ThisWorkbook.Sheets("Alldata")
Dim lRow As Long, lCol As Long, cpyrng As Range
lCol = Cells(1, Columns.Count).End(xlToLeft).Column
For i = 21 To lCol Step 20
If .Cells(1, i).Value <> "" And .Cells(1, i).Offset(, -1).Value = "" Then
lRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Set cpyrng = .Cells(1, i).CurrentRegion
cpyrng.Cut
Sheets("Sheet2").Cells(lRow, 1).Offset(2).Insert Shift:=xlDown
End If
Next i
End With
I want to remove duplicates based on the text in Column I and sum the values in Column C, the data in the other columns doesn't matter.
I do not want a pivot table and I am aware they are the preferred option for this type of thing.
An example of what I'd like to achieve:
I found VBA code and tried to modify it. It doesn't delete all the lines.
Sub Sum_and_Dedupe()
With Worksheets("data")
'deal with the block of data radiating out from A1
With .Cells(1, 1).CurrentRegion
'step off the header and make one column wider
With .Resize(.Rows.Count - 1, .Columns.Count + 1).Offset(1, 0)
.Columns(.Columns.Count).Formula = "=sumifs(c:c, i:i, i2)"
.Columns(3) = .Columns(.Columns.Count).Value
.Columns(.Columns.Count).Delete
End With
'remove duplicates
.RemoveDuplicates Columns:=Array(9), Header:=xlYes
End With
.UsedRange
End With
End Sub
This should be an answer to your question.
However, code might require adaptation if the range in which you look becomes very long.
Option Explicit
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Dim LastRow As Long, LastCol As Long, a As Double, i As Long
Dim Rng As Range
Dim Cell As Variant, Estimate As Variant
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
LastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Set Rng = ws.Range(ws.Cells(2, 9), ws.Cells(LastRow, 9))
For Each Cell In Rng
i = 0
a = 0
For Each Estimate In Rng
If Estimate.Value = Cell.Value Then
i = i + 1 'Count nr of intances
a = a + ws.Cells(Estimate.Row, 3).Value 'sum booking value
If i > 1 Then
ws.Rows(Estimate.Row).Delete
i = 1
LastRow = LastRow - 1
End If
End If
Next Estimate
ws.Cells(Cell.Row, 3).Value = a 'Enter sum in booked this week
Next Cell
End Sub
You'll either need to change your current sheet name to data, or change the first two lines of this code to fit your needs. sh = the data sheet that you showed us. osh = an output sheet that this code will generate. Note also if column C or I move you can update the positions easily by changing colBooked and colEstimate. If you have more than a thousand unique estimate entries then make the array number larger than 999.
Sub summariseEstimates()
Dim sh As String: sh = "data"
Dim osh As String: osh = "summary"
Dim colBooked As Integer: colBooked = 3
Dim colEstimate As Integer: colEstimate = 9
Dim myArray(999) As String
Dim shCheck As Worksheet
Dim output As Worksheet
Dim lastRow As Long
Dim a As Integer: a = 0
Dim b As Integer
Dim r As Long 'row anchor
Dim i As Integer 'sheets
'Build summary array:
With Worksheets(sh)
lastRow = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For r = 2 To lastRow
If r = 2 Then 'first entry
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
Else
For b = 0 To a
If VBA.LCase(VBA.Replace(.Cells(r, colEstimate), " ", "")) = VBA.LCase(VBA.Replace(VBA.Split(myArray(b), ",")(0), " ", "")) Then 'match
myArray(b) = VBA.Split(myArray(b), ",")(0) & "," & VBA.Split(myArray(b), ",")(1) + .Cells(r, colBooked)
Exit For
End If
Next b
If b = a + 1 Then 'completed loop = no match, create new array item:
a = a + 1
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
End If
End If
Next r
End With
'Create summary sheet:
On Error Resume Next
Set shCheck = Worksheets(osh)
If Err.Number <> 0 Then
On Error GoTo 0
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
Err.Clear
Else
On Error GoTo 0
If MsgBox("*" & osh & "* sheet already exists. Proceed to delete and recreate?", vbOKCancel, "Summary") = vbCancel Then
Exit Sub
Else
Application.DisplayAlerts = False
Worksheets(osh).Delete
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
End If
End If
'Output to summary sheet:
With Worksheets(osh)
.Cells(1, 1).Value = "ESTIMATE"
.Cells(1, 2).Value = "BOOKED THIS WEEK"
For b = 0 To a
.Cells(b + 2, 1).Value = VBA.Split(myArray(b), ",")(0)
.Cells(b + 2, 2).Value = VBA.Split(myArray(b), ",")(1)
Next b
.Columns("A:B").AutoFit
End With
End Sub
I have a macro that filters a range, and I have a range of values which I want to represent the number of rows being selected after the filter is applied.
I have most of the code sorted, im just getting stuck on selecting the visible rows only.
EG. Sheet 1 contains variable numbers (1, 2, 3 ,4 etc) which I have labelled as NOC1.
Now once the filter is applied it selects the correct number of rows, but also selects hidden cells. I just want it to select the visible cells only.
Here is the code:
Set TopVisibleCell = Rstatus.Offset(1).Rows.SpecialCells(xlCellTypeVisible).Rows(1)
TopVisibleCell.Select
Selection.Resize(Selection.Rows.Count + NOC1 - 1, _
Selection.Columns.Count).Copy
Any help would be greatly appreciated.
Thanks!
Edit:
Please excuse my poor description, it seems I didnt express myself clearly.
Please find link to Sample.xlsm which will hopefully shed some light on my problem.
Link : Sample Workbook
Thanks for your help
you can loop with a counter:
Sub FilterCDA()
Dim sh1 As Worksheet
Dim N As Long
Dim TopVisibleCell As Range
Dim sh2 As Worksheet
Dim HeaderRow As Long
Dim LastFilterRow As Long
Dim st As String
Dim rng1 As Range
Dim rng2 As Range
Dim rng3 As Range
Dim VTR As String
Dim W As Integer
Dim R As Integer
Dim NOC As Range
Dim NOC1 As Integer
Dim rSelect As Range
Dim rCell As Range
Set sh1 = Sheets("Request")
Set sh2 = Sheets("Request")
C = 2
Set NOC = sh2.Range("D2")
NOC1 = NOC.Value
LR = Worksheets("ORT").Range("A" & Rows.Count).End(xlUp).Row
Set Rstatus1 = Worksheets("ORT").Range("G2:G" & LR)
Set Rstatus = Worksheets("ORT").Range("A1:G" & LR)
N = sh1.Cells(Rows.Count, "C").End(xlUp).Row
Sheets("CSV").Cells.NumberFormat = "#"
For i = 2 To N
v = sh1.Cells(i, 3).Value
If v <> "" Then
st = st & v & ","
End If
Next i
st = Mid(st, 1, Len(st) - 1)
Arr1 = Split(st, ",")
Sheets("ORT").Activate
For i = LBound(Arr1) To UBound(Arr1)
Sheets("ORT").AutoFilterMode = False
With Sheets("ORT").Range("A:G")
.AutoFilter Field:=3, Criteria1:=Arr1(i), Operator:=xlFilterValues
End With
Fr = Worksheets("ORT").Range("C" & Rows.Count).End(xlUp).Row - 1
' No rows filtered then Fr = 0
If Fr > 0 Then
With Rstatus
Set rVis = .Resize(.Rows.Count - 1, 1).Offset(1).SpecialCells(xlCellTypeVisible)
End With
For Each rCell In rVis.Cells
If rSelect Is Nothing Then
Set rSelect = rCell.Resize(, Rstatus.Columns.Count)
Else
Set rSelect = Union(rSelect, rCell.Resize(, Rstatus.Columns.Count))
End If
lCounter = lCounter + 1
If lCounter >= NOC1 Then Exit For
Next rCell
rSelect.Copy
Sheets("CSV").Cells(Rows.Count, "A").End(xlUp).Offset(1).PasteSpecial xlPasteValues
ElseIf Fr = 0 Then
End If
Set NOC = NOC.Offset(1)
NOC1 = NOC.Value
Next i
Sheets("ORT").AutoFilterMode = False
Sheets("Request").Select
Range("E2").Select
ActiveCell.FormulaR1C1 = "=COUNTIF('CSV'!C[-2],'Request'!RC[-2])"
On Error Resume Next
Selection.AutoFill Destination:=Range("E2:E" & Range("C" & Rows.Count).End(xlUp).Row), Type:=xlFillCopy
Columns("E:E").Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("A1").Select
Sheets("Control").Select
Range("A1").Select
End Sub
If row #1 is the header row and you want to select the visible range of the AutoFilter and there is no "junk" below the filter in column A then:
Sub SelectVisibleA()
Dim NLastVisible As Long, r As Range
NLastVisible = Cells(Rows.Count, "A").End(xlUp).Row
Set r = Range("A2:A" & NLastVisible).Cells.SpecialCells(xlCellTypeVisible)
r.Select
End Sub
will select the visible material in column A...........you need to RESIZE to get additional columns.