How can I modify my code so that it runs quicker? - excel

I work a cumulative report that grows daily up to about 150,000 rows of data. I am trying to run a macro that will move the data from one defined sheet to another defined sheet. Unfortunately, it is taking an extremely long time and leaves my Excel window frozen.
I have been staring at this code trying to make it work for our needs for so long that I haven't tried anything different.
Sub Move()
Application.ScreenUpdating = False
Dim Check As Range, r As Long, lastrow2 As Long, lastrow As Long
lastrow = Worksheets("From TaxWise").UsedRange.Rows.Count
lastrow2 = Worksheets("State").UsedRange.Rows.Count
If lastrow2 = 1 Then lastrow2 = 0
For r = lastrow To 2 Step -1
If Not Range("L" & r).Value = "US" Then
Rows(r).Cut Destination:=Worksheets("State").Range("A" & lastrow2 + 1)
lastrow2 = lastrow2 + 1
Else:
End If
Next r
On Error Resume Next
ActiveWorkbook.Worksheets("From TaxWise").Columns("C:C").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
On Error GoTo 0
Application.ScreenUpdating = True
End Sub
Not sure what I need to adjust as I feel my current code is running through 150,000 records line by line to identify, cut and move.

This code took about two seconds to run on 150000 records with about 3000 equal to US.
You'll need to alter it to match your setup. eg: Names of the various worksheets; cell ranges in case your tables don't start at A1, slightly different syntax if your data is in Excel Tables rather than just ranges, etc
It uses Excel's built-in autofilter
The destination sheet has all of the lines except for those with US.
Option Explicit
Sub noUS()
Dim wsSrc As Worksheet, wsDest As Worksheet
Dim rSrc As Range, rDest As Range
Const filterColumn As Long = 4 'Change to 12 for column L
Dim LRC() As Long
Set wsSrc = Worksheets("sheet1")
Set wsDest = Worksheets("sheet2")
Set rDest = wsDest.Cells(1, 1)
wsDest.Cells.Clear
With wsSrc
'get last row and column of the source worksheet
LRC = LastRowCol(.Name)
'set the range
Set rSrc = .Range(.Cells(1, 1), .Cells(LRC(0), LRC(1)))
'set the filter
'first turn it off
.AutoFilterMode = False
'now set it for the range
rSrc.AutoFilter _
field:=filterColumn, _
Criteria1:="<>US", _
visibledropdown:=False
End With
Set rSrc = rSrc.SpecialCells(xlCellTypeVisible)
rSrc.Copy rDest
'turn off the autofilter
wsSrc.AutoFilterMode = False
End Sub
'--------------------------------------------
Private Function LastRowCol(Worksht As String) As Long()
Application.Volatile
Dim WS As Worksheet, R As Range
Dim LastRow As Long, LastCol As Long
Dim L(1) As Long
Set WS = Worksheets(Worksht)
With WS
Set R = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlValues, searchorder:=xlByRows, _
searchdirection:=xlPrevious)
If Not R Is Nothing Then
LastRow = R.Row
LastCol = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlValues, searchorder:=xlByColumns, _
searchdirection:=xlPrevious).Column
Else
LastRow = 1
LastCol = 1
End If
End With
L(0) = LastRow
L(1) = LastCol
LastRowCol = L
End Function
And if you want to have a separate sheet with the US rows, you can insert the following before the end of the Sub:
'now get the US rows
With wsSrc
Set rSrc = .Range(.Cells(1, 1), .Cells(LRC(0), LRC(1)))
.AutoFilterMode = False
rSrc.AutoFilter _
field:=filterColumn, _
Criteria1:="US", _
visibledropdown:=False
Set rSrc = rSrc.SpecialCells(xlCellTypeVisible)
rSrc.Copy rUS
.AutoFilterMode = False
End With
I prefer to maintain the original data, rather than deleting stuff from the source. But, if you like, after you've done the above, and you are happy with the result, merely delete wsSrc
Edit
The above code modified so you wind up with, what I think you want, which is worksheets("State") containing all of the non-US items; and worksheets("From TaxWise") containing all of the US items.
Instead of deleting non-contiguous rows, a very slow process, we filter the rows we want to a new worksheet; delete the original worksheet, and rename the new sheet.
Don't try this at home without a backup of your original data.
Option Explicit
Sub noUS()
Dim wsSrc As Worksheet, wsDest As Worksheet, wsUS As Worksheet
Dim rSrc As Range, rDest As Range, rUS As Range
Const filterColumn As Long = 12
Dim LRC() As Long
Set wsSrc = Worksheets("From TaxWise")
Set wsDest = Worksheets("State")
Set rDest = wsDest.Cells(1, 1)
wsDest.Cells.Clear
With wsSrc
'get last row and column of the source worksheet
LRC = LastRowCol(.Name)
'set the range
Set rSrc = .Range(.Cells(1, 1), .Cells(LRC(0), LRC(1)))
'set the filter
'first turn it off
.AutoFilterMode = False
'now set it for the range
rSrc.AutoFilter _
field:=filterColumn, _
Criteria1:="<>US", _
visibledropdown:=False
End With
Set rSrc = rSrc.SpecialCells(xlCellTypeVisible)
rSrc.Copy rDest
'turn off the autofilter
wsSrc.AutoFilterMode = False
'now get the US rows, may need to add worksheet
On Error Resume Next
Set wsUS = Worksheets("US")
If Err.Number = 9 Then
Worksheets.Add
ActiveSheet.Name = "US"
End If
Set wsUS = Worksheets("US")
Set rUS = wsUS.Cells(1, 1)
With wsSrc
Set rSrc = .Range(.Cells(1, 1), .Cells(LRC(0), LRC(1)))
.AutoFilterMode = False
rSrc.AutoFilter _
field:=filterColumn, _
Criteria1:="US", _
visibledropdown:=False
Set rSrc = rSrc.SpecialCells(xlCellTypeVisible)
rSrc.Copy rUS
.AutoFilterMode = False
End With
'Delete Taxwise and rename US sheets
Application.DisplayAlerts = False
wsSrc.Delete
wsUS.Name = "From TaxWise"
Application.DisplayAlerts = True
End Sub
'--------------------------------------------
Private Function LastRowCol(Worksht As String) As Long()
Application.Volatile
Dim WS As Worksheet, R As Range
Dim LastRow As Long, LastCol As Long
Dim L(1) As Long
Set WS = Worksheets(Worksht)
With WS
Set R = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlValues, searchorder:=xlByRows, _
searchdirection:=xlPrevious)
If Not R Is Nothing Then
LastRow = R.Row
LastCol = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlValues, searchorder:=xlByColumns, _
searchdirection:=xlPrevious).Column
Else
LastRow = 1
LastCol = 1
End If
End With
L(0) = LastRow
L(1) = LastCol
LastRowCol = L
End Function

You can filter and work with visible cells or you can avoid deleting rows inside your loop.
Say, for instance, that you have 500 cells that are not equal to US. You will then have 500 instances of copy/paste & deletions. This is highly inneficient.
Instead, add your target cells to a Union (collection of cells) and then outside of the loop, perform your operations on the collection. No matter how many rows are being targeted, you will have just one instance of copy, one instance of paste, and one instance of deletion.
Sub Moving()
Dim cs As Worksheet: Set cs = ThisWorkbook.Sheets("From TaxWise")
Dim ps As Worksheet: Set ps = ThisWorkbook.Sheets("State")
Dim MoveMe As Range, myCell As Range, LR2 As Long
Dim LR As Long: LR = cs.Range("L" & cs.Rows.Count).End(xlUp).Row
For Each myCell In cs.Range("L2:L" & LR)
If myCell <> "US" Then
If Not MoveMe Is Nothing Then
Set MoveMe = Union(MoveMe, myCell)
Else
Set MoveMe = myCell
End If
End If
Next myCell
If Not MoveMe Is Nothing Then
LR2 = ps.Range("A" & ps.Rows.Count).End(xlUp).Row
MoveMe.EntireRow.Copy
ps.Range("A" & LR2).PasteSpecial xlPasteValues
MoveMe.EntireRow.Delete
End If
End Sub

Move Rows
Union Version
Option Explicit
Sub Move()
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
Dim rngU As Range, r As Long, lastrow2 As Long, lastrow As Long
On Error GoTo ProcedureExit
With Worksheets("From Taxwise")
lastrow = .Cells(.Rows.Count, "L").End(xlUp).row
For r = 2 To lastrow
If Not .Range("L" & r).Value = "US" Then
If Not rngU Is Nothing Then
Set rngU = Union(rngU, .Cells(r, 1))
Else
Set rngU = .Cells(r, 1)
End If
End If
Next
End With
If Not rngU Is Nothing Then
With Worksheets("State")
lastrow2 = .Cells(.Rows.Count, "L").End(xlUp).row
rngU.EntireRow.Copy .Range("A" & lastrow2 + 1)
rngU.EntireRow.Delete
End With
Set rngU = Nothing
End If
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub

Related

How to edit this return multiple matches VBA code to input matches below?

I have this VBA code. It essentially is a vlookup but returns multiple matches. However, it inputs the matches next to the desWs value (“A1”). Goes column by column. How can I edit this to input the data below? Also I would like to be able edit the return matches location as the lookup value will be in “A1” but I would like some matches to go in column A and also column B. This will be done by running this code multiple times with different column numbers to search for on Sheet1.
I have tried editing the code a number of times with no luck. Either it doesn’t input the data down bottom or it doesn’t return all matches. Currently I use an array formula to do this but it slows down my file heavily. Here is the code. Thank you all.
Sub ReturnMultipleMatches()
Application.ScreenUpdating = False
Dim LastRow1 As Long, LastRow3 As Long, rng As Range, sAddr As String, _
Val As Range, lCol As Long, desWS As Worksheet, srcWS As Worksheet
Set desWS = Sheets("Sheet3")
Set scrWS = Sheets("Sheet1")
LastRow1 = scrWS.Cells.Find("*", SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
LastRow3 = desWS.Cells.Find("*", SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
For Each rng In desWS.Range("A1")
Set Val = scrWS.Range("A2:AH" & LastRow1).Find( _
rng, LookIn:=xlValues, lookat:=xlWhole)
If Not Val Is Nothing Then
sAddr = Val.Address
Do
lCol = desWS.Cells(rng.Row, desWS.Columns.Count).End(xlToLeft).Column + 1
desWS.Cells(rng.Row, lCol) = scrWS.Cells(Val.Row, 30)
Set Val = scrWS.Range("A2:AH" & LastRow1).FindNext(Val)
Loop While Val.Address <> sAddr
sAddr = ""
End If
Next rng
Application.ScreenUpdating = True
End Sub
Untested:
Sub ReturnMultipleMatches()
Application.ScreenUpdating = False
Dim LastRow1 As Long, rng As Range, sAddr As String, cDest As Range, _
Val As Range, lCol As Long, desWS As Worksheet, srcWS As Worksheet
Dim rngSrch As Range
Set srcWS = Sheets("Sheet1")
Set desWS = Sheets("Sheet3")
LastRow1 = srcWS.Cells.Find("*", SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
Set rngSrch = srcWS.Range("A2:AH" & LastRow1)
For Each rng In desWS.Range("A1").Cells
Set Val = rngSrch.Find(rng.Value, LookIn:=xlValues, lookat:=xlWhole)
If Not Val Is Nothing Then
'first empty cell below the value being searched
Set cDest = desWS.Cells(Rows.Count, rng.Column).End(xlUp).Offset(1, 0)
sAddr = Val.Address
Do
cDest.Value = Val.EntireRow.Cells(30).Value
Set cDest = cDest.Offset(1, 0) 'next row down
Set Val = rngSrch.FindNext(Val)
Loop While Val.Address <> sAddr
sAddr = ""
End If
Next rng
Application.ScreenUpdating = True
End Sub

Find all duplicates in a column of a certain value and return values from the next column

I am trying to make something that would look like this:
In the table on the right there will be all the unique records which will be stored in a certain area. However some record may be existing in more areas, and this information can be taken from the list in column A and B. The macro should take each unique record in column D and search for it in Column A, every time it finds it, should copy the location/area in column B and pasted next to the unique record in the table. I think I could do this with a loop, but what I created in the code below does not really works.
The second challenge is to make it understand that in a location has been copy into the table, the new found location needs to be pasted in the next free cell of that same unique record.
I am aware my code is a little scare but I would appreciate even just advice on which direction I should be looking... Thanks in advance!
Sub searcharea()
Dim UC As Variant, UCrng As Range, ra As Range
Set UCrng = Range("F2:F6")
For Each UC In UCrng
Set ra = Cells.Find(What:=UC, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
ra.Offset(0, 1).Copy Destination:=Range("E2")
Next
End Sub
I would suggest looping through all Rows (Columns A + B), e.g.:
For i = 1 to Rows.Count
'DoStuff
Next i
For each row, you copy the value of A into D, if it is not there already.
You can access the values like this:
Cells(i, "A").Value
Cells(i, "B").Value
For finding values in a column, see here. If you found a duplicate, use another loop to check which column (E, F, G,..) in your specific row is the first empty one, and past the value of column B there.
Take a try:
Option Explicit
Sub test()
Dim LastRowA As Long, LastRowD As Long, i As Long, rngColumn As Long
Dim rng As Range
With ThisWorkbook.Worksheets("Sheet1")
LastRowD = .Cells(.Rows.Count, "D").End(xlUp).Row
.Range("D2:J" & LastRowD).ClearContents
LastRowA = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To LastRowA
LastRowD = .Cells(.Rows.Count, "D").End(xlUp).Row
Set rng = .Range("D1:D" & LastRowD).Find(.Range("A" & i).Value, LookIn:=xlValues, lookat:=xlWhole)
If Not rng Is Nothing Then
rngColumn = .Cells(rng.Row, .Columns.Count).End(xlToLeft).Column
Cells(rng.Row, rngColumn + 1).Value = .Range("B" & i).Value
Else
.Range("D" & LastRowD + 1).Value = .Range("A" & i).Value
.Range("E" & LastRowD + 1).Value = .Range("B" & i).Value
End If
Next i
End With
End Sub
I think this code will do what you want. Please try it.
Option Explicit
Sub SortToColumns()
' Variatus #STO 30 Jan 2020
Dim WsS As Worksheet ' Source
Dim WsT As Worksheet ' Target
Dim Rng As Range
Dim Fn As String, An As String ' File name, Area name
Dim Rls As Long
Dim Rs As Long
Dim Rt As Long, Ct As Long
With ThisWorkbook ' change as required
Set WsS = .Worksheets("Sheet1") ' change as required
Set WsT = .Worksheets("Sheet2") ' change as required
End With
With WsT
' delete all but the caption row
.Range(.Cells(2, 1), .Cells(.Rows.Count, "A").End(xlUp)).EntireRow.ClearContents
End With
Application.ScreenUpdating = False
With WsS
' find last row of source data
Rls = .Cells(.Rows.Count, "A").End(xlUp).Row
For Rs = 2 To Rls ' start from row 2 (row 1 is caption)
Fn = .Cells(Rs, "A").Value
An = .Cells(Rs, "B").Value
If FileNameRow(Fn, WsT, Rt) Then
' add to existing item
With WsT
Ct = .Cells(Rt, .Columns.Count).End(xlToLeft).Column
Set Rng = .Range(.Cells(Rt, "B"), .Cells(Rt, Ct))
End With
With Rng
Set Rng = .Find(An, .Cells(.Cells.Count), xlValues, xlWhole, xlByRows, xlNext)
End With
' skip if Area exists
If Rng Is Nothing Then WsT.Cells(Rt, Ct + 1).Value = An
Else
' is new item
WsT.Cells(Rt, "A").Value = Fn
WsT.Cells(Rt, "B").Value = An
End If
Next Rs
End With
Application.ScreenUpdating = True
End Sub
Private Function FileNameRow(Fn As String, _
WsT As Worksheet, _
Rt As Long) As Boolean
' Rt is a return Long
' return True if item exists (found)
Dim Fnd As Range
Dim Rng As Range
Dim R As Long
With WsT
R = .Cells(.Rows.Count, "A").End(xlUp).Row
Set Rng = .Range(.Cells(2, "A"), .Cells(R, "A"))
Set Fnd = Rng.Find(Fn, Rng.Cells(Rng.Cells.Count), xlValues, xlWhole, xlByRows, xlNext)
If Fnd Is Nothing Then
Rt = Application.Max(.Cells(.Rows.Count, "A").End(xlUp).Row + 1, 2)
Else
Rt = Fnd.Row
FileNameRow = True
End If
End With
End Function

Compare and Copy from one spreadsheet to another

There are two Excel workbooks, Master and Survey Responses.
I have to loop through each row in Survey Responses, to select the value from the 4th column and compare it to the 4th column in Master. If there is no match then copy the complete row from Survey Responses to the end of Master. For the first time there will be no rows in Master so all rows must be copied from Survey Responses.
Survey Responses
The below code does not loop through all rows and if I run it a second time it copies all rows without performing the comparison.
'''''Define Object for Target Workbook
Dim Target_Workbook As Workbook
Dim Source_Workbook As Workbook
Dim Source_Path As String
'''''Assign the Workbook File Name along with its Path
Source_Path = "C:\Users\Survey Responses\Survey Response.xls"
Set Source_Workbook = Workbooks.Open(Source_Path)
Set Target_Workbook = ThisWorkbook
'''''With Source_Workbook object now, it is possible to pull any data from it
'''''Read Data from Source File
'''''Logic to select unique rows only
Dim rngSource As Range, rngTarget As Range, cellSource As Range, cellTarget As Range
Set rngSource = Source_Workbook.Sheets(1).Range("Responses")
Set rngTarget = Target_Workbook.Sheets(2).Range("Responses")
Dim rowNr_target As Integer, Rng As Range
With Target_Workbook.Sheets(2)
rowNr_target = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
Dim counter As Integer, found As Boolean, inner_counter As Integer
counter = 1
For Each cellSource In rngSource.Rows
'On Error Resume Next
If cellSource.Cells(counter, 1).Value = "" Then
Exit For
End If
found = False
inner_counter = 1
For Each cellTarget In rngTarget.Rows
If cellTarget.Cells(inner_counter, 1).Value = "" Then
Exit For
End If
''''test = Application.WorksheetFunction.VLookup(test1, rngTarget, 1, False)
If (cellSource.Cells(counter, 4) = cellTarget.Cells(inner_counter, 4)) Then
found = True
Exit For
End If
inner_counter = inner_counter + 1
Next
If (found = False) Then
cellSource.EntireRow.Copy
If (rowNr_target > 1) Then
rngTarget.Rows(rowNr_target + 1).Insert
Else
rngTarget.Rows(rowNr_target).Insert
End If
rowNr_target = rowNr_target + 1
End If
counter = counter + 1
'On Error GoTo 0
Next
'''''Target_Workbook.Sheets(2).Range("Responses").Value = Source_data
'''''Close Target Workbook
Source_Workbook.Save
Target_Workbook.Save
''''Source_Workbook.Close False
'''''Process Completed
MsgBox "Task Completed"
Updated code:
Dim cel As Range
Dim rng As Range
Dim r As Range
Dim lastrow As Long
Dim Target_Workbook As Workbook
Dim Source_Workbook As Workbook
Dim Source_Path As String
'''''Assign the Workbook File Name along with its Path
Source_Path = "C:\Users\Survey Responses\Survey Response.xls"
Set Source_Workbook = Workbooks.Open(Source_Path)
Set Target_Workbook = ThisWorkbook
Dim rngSource As Range, rngTarget As Range, cellSource As Range, cellTarget As Range
Set rngSource = Source_Workbook.Sheets(1).Range("Responses")
Set rngTarget = Target_Workbook.Sheets(2).Range("Responses")
With Target_Workbook.Sheets(2)
lastrow = .Cells(.Rows.Count, 1).End(xlUp).Row
For Each cel In Source_Workbook.Sheets(1).Range("D:D")
If cel.Value = "" Then
Exit For
End If
Set r = .Range("D:D").Find(What:=cel, LookIn:=xlFormulas, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If r Is Nothing Then
cel.EntireRow.Copy
rngTarget.Rows(lastrow).Insert
''If Not rng Is Nothing Then Set rng = Union(rng, cel) Else Set rng = cel
End If
Next cel
''rng.Copy.Range("A" & lastrow).PasteSpecial xlPasteValues
End With
'''''Close Target Workbook
Source_Workbook.Save
Target_Workbook.Save
''''Source_Workbook.Close False
'''''Process Completed
MsgBox "Task Completed"
This is untested code but it should help you with anything you already have. You will need to adjust the ranges to suit yourself, but it will loop through one sheet and collect values that dont exists and then copy them to another sheet.
Try this,
Sub dave()
Dim cel As Range
Dim rng As Range
Dim r As Range
Dim lastrow As Long
With Sheets("Master")
lastrow = .Cells(.Rows.Count, 1).End(xlUp).Row
For Each cel In Sheets("Sheet1").Range("D1:D22")
Set r = .Range("D:D").Find(What:=cel, LookIn:=xlFormulas, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If r Is Nothing Then
If Not rng Is Nothing Then Set rng = Union(rng, cel) Else Set rng = cel
End If
Next cel
rng.Copy
.Range("A" & lastrow).PasteSpecial xlPasteValues
End With
End Sub

Summarize data using Excel VBA

I am a beginner to VBA. On sheet one I have data formatted like this:
SHEET 1
What I want to do is use VBA to spit out the following graph which dynamically populates the region depending on how many it finds:
SHEET 2
This is my first bit of VBA so I am struggling a bit. This is my idea of how to approach this problem:
My idea was to scroll down the string in my data in sheet1 col A and determine if it's a date we have seen before or not:
Public Sub Test()
ActiveSheet.Range("Sheet1!A1:A5000").AdvancedFilter Action:=xlFilterCopy, CopyToRange.Range("Sheet2!A1"), Unique:=True
End Sub
Questions
Is this flow diagram taking the right approach?
If so, how do I implement this kind of "Is this unique, if so do this, if not do this" kind of setup.
How can I start this code so I have something to build on?
This is what I have so far:
https://gist.githubusercontent.com/employ/af67485b8acddce419a2/raw/6dda3bb1841517731867baec56a0bf2ecf7733a7/gistfile1.txt
For different approach please see below:
Sheet 1 layout (Source):
Sheet 2 Layout (Target):
Sub SalesRegion()
Dim ws1, ws2 As Worksheet
Dim wb As Workbook
Dim ws1LastRow, ws2LastRow, salesVal As Long
Dim destFind, dateFind As Range
Dim destStr As String
Dim dateStr As Date
Dim targetCol, targetRow As Long
Set wb = ActiveWorkbook '<- Your workbook
Set ws1 = wb.Sheets("Sheet1") '<- Your source worksheet
Set ws2 = wb.Sheets("Sheet2") '<- Your destination worksheet
ws1LastRow = ws1.Range("A" & Rows.Count).End(xlUp).Row
For i = 2 To ws1LastRow
destStr = ws1.Range("C" & i).Value
dateStr = ws1.Range("A" & i).Value
salesVal = ws1.Range("B" & i).Value
With ws2.Rows("1:1") '<- row on destination sheet which contains countries
Set destFind = .Find(What:=destStr, _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not destFind Is Nothing Then
targetCol = destFind.Column
With ws2.Columns("A:A") '<- Column on destination sheet which contains months
'You may need to adjust date format below depending on your regional settings
Set dateFind = .Find(What:=Format(ws1.Range("A" & i).Value, "MMM-yy"), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not dateFind Is Nothing Then
targetRow = dateFind.Row
ws2.Cells(targetRow, targetCol).Value = ws2.Cells(targetRow, targetCol).Value + salesVal
Else
ws2.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = dateStr
targetRow = ws2.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Row
ws2.Cells(targetRow, targetCol).Value = salesVal
End If
End With
Else
ws2.Cells(1, Columns.Count).End(xlToLeft).Offset(0, 1).Value = destStr
targetCol = ws2.Cells(1, Columns.Count).End(xlToLeft).Column
With ws2.Columns("A:A") '<- Column on destination sheet which contains months
'You may need to adjust date format below depending on your regional settings
Set dateFind = .Find(What:=Format(ws1.Range("A" & i).Value, "MMM-yy"), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not dateFind Is Nothing Then
targetRow = dateFind.Row
ws2.Cells(targetRow, targetCol).Value = ws2.Cells(targetRow, targetCol).Value + salesVal
Else
ws2.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = dateStr
targetRow = ws2.Range("A" & Rows.Count).End(xlUp).Row
ws2.Cells(targetRow, targetCol).Value = salesVal
End If
End With
End If
End With
Next
End Sub
First, I agree with the others that you should look for a solution using the built-in capabilities of the Pivot Table.
Since you've mentioned that it does not meet your expectations, I threw together some code that works to summarize the data as you've requested. Let me know if it does the trick, if you need any added help adjusting it for your needs, or if you have any other general questions.
Sub SummarizeInNewSheet()
Dim wsOrigin As Worksheet
Dim wsDest As Worksheet
Dim rngOrigin As Range
Dim oDict As Object
Dim cel As Range
Dim rngLocations As Range
Dim nLastRow As Long
Dim nLastCol As Long
Dim rngInterior As Range
Dim rngAllDates As Range
Dim rngAllLocations As Range
Dim rngAllSales As Range
Application.ScreenUpdating = False
Set wsOrigin = Worksheets("Sheet1")
Set wsDest = Worksheets("Sheet2")
Set rngOrigin = wsOrigin.Range("A1").CurrentRegion
Intersect(rngOrigin, wsOrigin.Columns(1)).Copy wsDest.Range("A1")
wsDest.Range(wsDest.Range("A1"), wsDest.Range("A1").End(xlDown)).RemoveDuplicates Columns:=1, Header:=xlYes
Set oDict = CreateObject("Scripting.Dictionary")
Set rngLocations = wsDest.Range("B1")
For Each cel In Intersect(rngOrigin, wsOrigin.Columns(3))
If cel.Row = 1 Then
Else
If oDict.exists(cel.Value) Then
'Do nothing for now
Else
oDict.Add cel.Value, 0
rngLocations.Value = cel.Value
Set rngLocations = rngLocations.Offset(, 1)
End If
End If
Next cel
nLastRow = wsDest.Cells(Rows.Count, 1).End(xlUp).Row
nLastCol = wsDest.Cells(1, Columns.Count).End(xlToLeft).Column
Set rngInterior = wsDest.Range(wsDest.Range("B2"), wsDest.Cells(nLastRow, nLastCol))
Set rngAllDates = wsOrigin.Range(wsOrigin.Range("A2"), wsOrigin.Range("A2").End(xlDown))
Set rngAllSales = wsOrigin.Range(wsOrigin.Range("B2"), wsOrigin.Range("B2").End(xlDown))
Set rngAllLocations = wsOrigin.Range(wsOrigin.Range("C2"), wsOrigin.Range("C2").End(xlDown))
For Each cel In rngInterior
cel.Value = Application.WorksheetFunction.SumIfs(rngAllSales, rngAllDates, wsDest.Cells(cel.Row, 1), rngAllLocations, wsDest.Cells(1, cel.Column))
Next cel
Application.ScreenUpdating = True
End Sub

Copy and paste rows between worksheets

What I want to achieve is to copy data from WS1 to WS3 based on certain criteria.
I have 2 worksheets:
WS1 = RAW DATA
WS2 = ATLAS DATA
In columns A of both there are unique identifiers. What I want to do is to create WS3=Reconciliation. Then look up values in WS2 against WS1. Where a match is found I want to copy row(s) from WS1 to WS3 that all
I have reverse engineered some code and came up with one below
Sub CopyAndPaste()
Dim x As String, CpyRng As Range
Dim mFIND As Range, mFIRST As Range
With Sheets("RAW DATA")
Range("A:A").Select
On Error Resume Next
End With
With Sheets("ATLAS DATA")
Set mFIND = .Range("A:A").Find(x, LookIn:=xlValues, LookAt:=xlWhole)
If Not mFIND Is Nothing Then
Set CpyRng = mFIND
Set mFIRST = mFIND
Do
Set CpyRng = Union(CpyRng, mFIND)
Set mFIND = .Range("A:A").FindNext(mFIND)
Loop Until mFIND.Address = mFIRST.Address
CpyRng.EntireRow.Copy Sheets("Rec").Range("A" & Rows.Count).End(xlUp).Offset(1)
End If
End With
End Sub
Based on your description of your problem; try this
Option Explicit
Sub CopyAndPaste()
Application.ScreenUpdating = False
Dim i As Long, j As Long, lastRow1 As Long, lastRow2 As Long, cnt As Long
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Set ws1 = ActiveWorkbook.Sheets("RAW DATA")
Set ws2 = ActiveWorkbook.Sheets("ATLAS DATA")
Set ws3 = ActiveWorkbook.Sheets("Reconciliation")
lastRow1 = ws1.Range("A" & Rows.Count).End(xlUp).Row
lastRow2 = ws2.Range("A" & Rows.Count).End(xlUp).Row
cnt = 1
For i = 1 To lastRow1
For j = 1 To lastRow2
If StrComp(CStr(ws2.Range("A" & j).Value), _
CStr(ws1.Range("A" & i).Value), _
vbTextCompare) = 0 Then
ws1.Activate
ws1.Rows(i).Select
Selection.Copy
ws3.Activate
ws3.Range("A" & cnt).Select
Selection.PasteSpecial Paste:=xlPasteAllUsingSourceTheme
Application.CutCopyMode = False
cnt = cnt + 1
End If
Next j
Next i
Application.ScreenUpdating = True
End Sub

Resources