I have would like some help regarding the following problem. Every quarter we a have excel sheets sent to us with client info containing rows often more than a 1000. I've managed to write a code that deletes duplicate rows that are a 100% match, however, a considerable portion still remains due to the following:
A new code I have found kinda works, however I would need some help tweaking it, as it does the following:
It deletes the duplicate and merges the cells, however, if one cell value (in this case Marketing) appears both times it keeps it twice. Also, it does not retain other info like mail/name/phone etc.
Here's the code itself:
Sub Main()
Dim Source As Worksheet: Set Source = ThisWorkbook.Worksheets("Sheet1")
Dim Destination As Worksheet: Set Destination = ThisWorkbook.Worksheets("Sheet2")
Dim Records As Object: Set Records = CreateObject("Scripting.Dictionary")
Dim Data As Variant
Dim Index As Long
Dim Row As Integer: Row = 1
Data = Source.Range("A1", "E" & Source.Rows(Source.UsedRange.Rows.Count).Row).Value2
For Index = LBound(Data, 1) To UBound(Data, 1)
If Records.Exists(Data(Index, 1)) Then
Destination.Cells(Records(Data(Index, 1)), 5).Value2 = Destination.Cells(Records(Data(Index, 1)), 5).Value2 & ", " & Data(Index, 5)
Else
Records.Add Data(Index, 1), Row
Destination.Cells(Row, 1).Value2 = Data(Index, 1)
Destination.Cells(Row, 5).Value2 = Data(Index, 5)
Row = Row + 1
End If
Next Index
Set Records = Nothing
End Sub
I was wondering if there is a way to tackle this problem, or is it too complicated? If the latter, no problems, only deleting the duplicates works fine and reduces work hours a lot.
Thank you for any input and comment!
I use a Dictionary to remove duplicates in a comma delimit string. Email, code and Country are also copied over to the Destination worksheet.
Sub Main()
Dim Source As Worksheet: Set Source = ThisWorkbook.Worksheets("Sheet1")
Dim Destination As Worksheet: Set Destination = ThisWorkbook.Worksheets("Sheet2")
Dim Records As Object: Set Records = CreateObject("Scripting.Dictionary")
Dim Data As Variant
Dim Index As Long
Dim Row As Integer: Row = 1
Data = Source.Range("A1", "E" & Source.Rows(Source.UsedRange.Rows.Count).Row).Value2
With Destination
For Index = LBound(Data, 1) To UBound(Data, 1)
If Records.Exists(Data(Index, 1)) Then
Destination.Cells(Records(Data(Index, 1)), 5).Value2 = removeDuplicates(Destination.Cells(Records(Data(Index, 1)), 5).Value2 & ", " & Data(Index, 5))
Else
Records.Add Data(Index, 1), Row
Destination.Cells(Row, 1).Value2 = Data(Index, 1)
Destination.Cells(Row, 2).Value2 = Data(Index, 2)
Destination.Cells(Row, 3).Value2 = Data(Index, 3)
Destination.Cells(Row, 4).Value2 = Data(Index, 4)
Destination.Cells(Row, 5).Value2 = Data(Index, 5)
Row = Row + 1
End If
Next Index
End With
Set Records = Nothing
End Sub
Function removeDuplicates(values As String)
Dim v As Variant
Dim d As Object
Set d = CreateObject("Scripting.Dictionary")
For Each v In Split(values, ",")
If v <> "" Then d(v) = 1
Next
removeDuplicates = Join(d.Keys, ", ")
Set d = Nothing
End Function
Try the following
If Records.Exists(Data(Index, 1)) Then
If InStr(Destination.Cells(Records(Data(Index, 1)), 5).Value2, Data(Index, 5)) = 0 Then
Destination.Cells(Records(Data(Index, 1)), 5).Value2 = Destination.Cells(Records(Data(Index, 1)), 5).Value2 & ", " & Data(Index, 5)
End if
...
InStr searches for a certain String in another, and returns the position at which the string is found. Hence, if Marketing not found, instr will return 0 and it will be added to the cell. If it is already there, Instr will return something largern than 0 and it will not be added again.
Update If you have more records with more than one unit, try this
UnitFull = Data(Index, 5)
Do Until Len(UnitFull) = 0
If InStr(UnitFull, ",") > 0 Then
Unit = Left(UnitFull, Instr(UnitFull, ",") - 1)
UnitFull = Trim(Right(UnitFull, Len(UnitFull) - InStr(UnitFull, ",")))
Else
Unit = UnitFull
UnitFull = ""
End If
Destination.Cells(Records(Data(Index, 1)), 5).Value2 = Destination.Cells(Records(Data(Index, 1)), 5).Value2 & ", " & Unit
Unit = ""
Loop
Related
I have a dataset of 55.000 rows with 35.000 email-addresses of which 31.000 are unique, so a couple of users occupy multiple rows. I need to find the rows of these users and add them to a class object.
Loading the email column into an array and performing a MATCH/INDEX lookup took 200 seconds. It's acceptable for now, but definitely not fast enough for it's intended use of 200-500K datasets.
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
Dim dict As Dictionary
Set dict = CreateObject("scripting.dictionary")
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
Set UserRange = Range(Cells(2, 11), Cells(LastRow, 11))
For Each cell In UserRange
dict(cell.value) = dict(cell.value) + 1
Next
Debug.Print "Number of users: " & dict.Count
UserArray = Range(Cells(2, 11), Cells(LastRow, 11))
UserArray = WorksheetFunction.Transpose(WorksheetFunction.Transpose(WorksheetFunction.Transpose(UserArray)))
For Each User In dict
Dim UserIndex() As Variant
ReDim UserIndex(1 To dict(User))
For i = 1 To dict(User)
Row = WorksheetFunction.Match(User, UserArray, 0)
UserIndex(i) = Row
UserArray(Row) = Empty
Next
For i = LBound(UserIndex) To UBound(UserIndex)
Debug.Print User, UserIndex(i)
Next
Next
SecondsElapsed = Round(Timer - StartTime, 2)
Debug.Print "This code ran successfully in " & SecondsElapsed & " seconds"
I could probably build a block-based index based on source (every imported file +-10.000 entries has a start- and endrow) and speed it up by looking only in the appropriate block. But maybe there is another way?
Here's a different approach which is fairly fast:
Sub Lister()
Dim t, i, m, arr, rng, dict As Object, dictDupes As Object, usr, v
Set dict = CreateObject("scripting.dictionary")
Set dictDupes = CreateObject("scripting.dictionary")
Set rng = Range("A1:A500000")
'create some dummy data (0.5M rows)
With rng
.Formula = "=""USER_"" & ROUND(RAND()*5000,0) & ""_"" & ROUND(RAND()*3000,0)"
.Value = .Value
End With
t = Timer
arr = rng.Value
For i = 1 To UBound(arr, 1)
usr = arr(i, 1)
If Not dict.exists(usr) Then
dict.Add usr, i
Else
If Not dictDupes.exists(usr) Then dictDupes.Add usr, dict(usr)
dictDupes(usr) = dictDupes(usr) & "|" & i
End If
Next i
For Each usr In dictDupes
v = dictDupes(usr)
'Debug.Print "----" & usr & "---"
'Debug.Print Join(Split(v, "|"), ", ")
Next usr
Debug.Print dict.Count, dictDupes.Count
Debug.Print "Done in", Timer - t
End Sub
Completes in about 20-25 sec
Another note:
If you want to use Match then it's significantly faster to leave your data on the worksheet instead of running Match against an array.
Sub TestMatch()
Dim t, i, m, arr, rng
Set rng = Range("A1:A50000")
With rng
.Formula = "=ROUND(RAND()*30000,0)"
.Value = .Value
End With
t = Timer
For i = 1 To 10000
m = Application.Match(i, rng, 0)
Next i
Debug.Print "sheet", Timer - t
arr = rng.Value
t = Timer
For i = 1 To 10000
arr = rng.Value
m = Application.Match(i, arr, 0)
Next i
Debug.Print "array", Timer - t
End Sub
Output:
sheet 3.644531
array 131.9453
So the array is about 35x slower.
Thanks to Tim I got a solution going:
Dim dict As Dictionary
Set dict = CreateObject("scripting.dictionary")
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
Set UserRange = Range(Cells(2, 11), Cells(LastRow, 11))
For Each cell In UserRange
dict(cell.value) = dict(cell.value) + 1
Next
Debug.Print "Number of users: " & dict.Count
t = Timer
For Each User In dict
Set Profile = New UserProfile
Profile.Count = dict(User)
Dim UserIndex() As Variant
ReDim UserIndex(1 To dict(User))
For i = 1 To dict(User)
Row = WorksheetFunction.Match(User, UserRange, 0)
UserIndex(i) = Row
Next
For i = LBound(UserIndex) To UBound(UserIndex)
Dim Purchase() As Variant
ReDim Purchase(1 To LastCol) As Variant
Purchase = Range(Cells(UserIndex(i) + 1, 1), Cells(UserIndex(i) + 1, LastCol))
Profile.Add Purchase
Next
Next
Debug.Print "Match/Index loop completed in ", Timer - t
Turns out that matching on a range instead of an array is much faster. And so is reading from a range instead of performing an WorksheetFunction.Index on an array. These results were both unexpected to me, as I thought reading/writing to the workbook generally slows things down. I also added a (1 , to thePurchase array readouts in my UserProfile class in order to ditch the Transpose.
Profiling for the whole 55K dataset completed in only 23 seconds!
I have the following objective:
Loop through a huge excel sheet (200,000+ rows)
Find some data based on matching parameters (the original file is an xml file, so structured data... but I am using a Mac, where the XML parser is not supported)
Copy the data between double quote related to each variable
Paste the value in the relative column
Additional constraints I have to face:
Every value to copy is between double quotes (this is "good news", helping me to identify the right data to copy and paste)
Imagine the txt. data as a list of data objects (=> it's sequence of purchases, with related info, made by customers). The macro should be able to loop through the list and copy paste the data, starting a new row every time a new ID purchase comes up. Good news is that every purchase is marked by a unique ID.
I’m providing below an example of input and output. I would really be grateful if someone could help me on this.
//INPUT
<SequenceNumber="1">
<PurchaseSegment DayDateTime="2020-02-29T06:45:00" ArrivalDateTime="2020-02-29T09:40:00" StopQuantity="0" PurchaseNumber="229" ElapsedTime="115">"
<DayPoS LocationCode="AAA" DockID="4" />"
<ArrivalPoS LocationCode="CCC" />"
</SequenceNumber>
<SequenceNumber="2">
<PurchaseSegment DayDateTime="2019-09-28T06:41:00" ArrivalDateTime="2020-02-29T09:40:00" StopQuantity="1" PurchaseNumber="123" ElapsedTime="115">"
<DayPoS LocationCode="AAA" DockID="3" />"
<ArrivalPoS LocationCode="QQC" />"
</SequenceNumber>
//EXPECTED OUTPUT (by running the VBA macro)
Here you can find also my VBA attempt, I leveraged some VBA code I already found, but didn't succeed.
Public Sub TextDataToColumn()
Dim val As Variant val = "PurchaseSegment DayDateTime" // it would be great to have a list of paramaters here...
Set c = Cells.Find(val, LookIn:=xlValues, MatchCase:=False)
If Not c Is Nothing Then
Do
MsgBox "Value of val is found at " & c.Address & vbCrLf & c.Offset(0, 1).Value & vbCrLf & c.Offset(0, 2).Value
Set c = Cells.FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstaddress
End If
End Sub
You can parse your text using VBA text functions.
As I mentioned in my comments, what you posted is NOT a valid XML document.
I adapted what I would have done using a Dictionary and Classes, to using a Collection and Array. (Although there is a Mac add-in to enable use of dictionary object).
After pre-processing the text lines to make it easier to parse, we loop through all the text lines and store the appropriate items in defined locations in the array.
We collect each row of item into the collection object, and then output them onto a worksheet.
It works for the sample data you posted, but if your data is, in addition to being invalid xml, also has irregularities in the naming and formatting of the different nodes, you'll need a more sophisticated parsing method.
Option Explicit
Option Compare Text
Sub splitSeq()
Dim cS As Collection
Dim WB As Workbook, wsSrc As Worksheet, wsRes As Worksheet
Dim rRes As Range
Dim vSrc As Variant, vRes As Variant
Dim I As Long, v, w, x
'Set workbook, worksheet and range parameters
Set WB = ThisWorkbook
With WB
Set wsSrc = .Worksheets("Sheet4") 'or wherever the data exists
On Error Resume Next 'add a sheet if not present
Set wsRes = Worksheets("Results")
Select Case Err.Number
Case 9 'need to add a sheet
Set wsRes = WB.Worksheets.Add
wsRes.Name = "Results"
Case Is <> 0 'Something else went wrong
MsgBox "Error number " & Err.Number & vbLf & Err.Description
Err.Clear
End Select
End With
'set results range
Set rRes = wsRes.Cells(1, 1)
'read data into array for processing speed
'assuming all data is in column A
With wsSrc
vSrc = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With
'loop through data and save the Sequence objects
'Each starts with <sequence and ends with </sequence
Set cS = New Collection
For I = 1 To UBound(vSrc, 1)
If vSrc(I, 1) Like "<Sequence*" Then
ReDim vRes(1 To 8) 're-initialize array
Else
'Remove confusing spaces in node names and <> in attributes
vSrc(I, 1) = Replace(vSrc(I, 1), "Segment DayDate", "SegmentDayDate")
vSrc(I, 1) = Replace(vSrc(I, 1), "Pos Location", "PosLocation")
vSrc(I, 1) = Replace(vSrc(I, 1), "<", "")
vSrc(I, 1) = Replace(vSrc(I, 1), ">""", "")
vSrc(I, 1) = Replace(vSrc(I, 1), ">", "")
v = Split(vSrc(I, 1))
For Each w In v
x = Split(w, "=")
'Remove leading and trailing double quotes
If Left(x(1), 1) = """" And Right(x(1), 1) = """" Then
x(1) = Mid(x(1), 2)
x(1) = Left(x(1), Len(x(1)) - 1)
End If
Select Case x(0)
Case "PurchaseSegmentDayDateTime"
vRes(1) = x(1)
Case "ArrivalDateTime"
vRes(2) = x(1)
Case "StopQuantity"
vRes(3) = x(1)
Case "PurchaseNumber"
vRes(4) = x(1)
Case "ElapsedTime"
vRes(5) = x(1)
Case "DayPosLocationCode"
vRes(6) = x(1)
Case "ArrivalPosLocationCode"
vRes(8) = x(1)
Case "DockID"
vRes(7) = x(1)
Case "/SequenceNumber"
cS.Add vRes
End Select
Next w
End If
Next I
'set up results array
ReDim vRes(0 To cS.Count, 1 To 8)
'Headers
vRes(0, 1) = "PurchaseSegment DayDateTime"
vRes(0, 2) = "ArrivalDateTime"
vRes(0, 3) = "StopQuantity"
vRes(0, 4) = "PurchaseNumber"
vRes(0, 5) = "ElapsedTime"
vRes(0, 6) = "DayPoS LocationCode"
vRes(0, 7) = "DockID"
vRes(0, 8) = "ArrivalPoS LocationCode"
'fill in the data
I = 0
For Each v In cS
I = I + 1
With v
vRes(I, 1) = v(1)
vRes(I, 2) = v(2)
vRes(I, 3) = v(3)
vRes(I, 4) = v(4)
vRes(I, 5) = v(5)
vRes(I, 6) = v(6)
vRes(I, 7) = v(7)
vRes(I, 8) = v(8)
End With
Next v
'Set Results range
Set rRes = rRes.Resize(UBound(vRes, 1) + 1, UBound(vRes, 2))
'Write and format results
With rRes
.EntireColumn.Clear
.Value2 = vRes
.Style = "Output"
.EntireColumn.AutoFit
End With
End Sub
I am trying to remove rows from a spreadsheet in VBA if the sum total of value exceeds a specific amount.
For example, if I have the following data, names in A1 down and values in A2 down:
I would like to remove all rows where the total sum of the value in row A does not reach 10 or above in row B, this would leave the following results:
Thomas = 18 and John = 15 so all rows with Thomas and John are kept.
All other rows would be deleted.
Please note that I will always know that the data is in row A and B but I do not know how many rows there will be and need to execute until the first blank cell.
It worked. You can see this here:
Sub run()
Dim rowIndex, countSameRow, sumSameRow As Integer
sumSameRow = Cells(1, 2)
rowIndex = 2
countSameRow = 1
While IsEmpty(Cells(rowIndex, 1)) = False
If (Cells(rowIndex, 1) = Cells(rowIndex - 1, 1)) Then
sumSameRow = sumSameRow + Cells(rowIndex, 2)
countSameRow = countSameRow + 1
Else
If (sumSameRow < 10) Then
Rows(rowIndex - 1 & ":" & rowIndex - countSameRow).Delete
rowIndex = rowIndex - countSameRow
End If
countSameRow = 1
sumSameRow = Cells(rowIndex, 2)
End If
If IsEmpty(Cells(rowIndex + 1, 1)) Then
If (sumSameRow < 10) Then
Rows(rowIndex & ":" & rowIndex - countSameRow + 1).Delete
End If
End If
rowIndex = rowIndex + 1
Wend
End Sub
Totally agree you should write your own code first, but I couldn't help but write some starting code for you. See if the below fits your purpose:
Sub Test()
Dim lr As Long, x As Long
Dim arr As Variant, rng As Range
Dim dict1 As Object: Set dict1 = CreateObject("Scripting.Dictionary")
Dim dict2 As Object: Set dict2 = CreateObject("Scripting.Dictionary")
With Sheet1 'Change according to your sheets CodeName
'Get all of your data form A:B in memory (array)
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
arr = .Range("A1:B" & lr)
'Step through the array and fill up our two dictionaries
For x = LBound(arr) To UBound(arr)
If dict1(arr(x, 1)) <> "" Then
dict1(arr(x, 1)) = Join(Array(dict1(arr(x, 1)), x & ":" & x), ",")
Else
dict1(arr(x, 1)) = x & ":" & x
End If
dict2(arr(x, 1)) = dict2(arr(x, 1)) + arr(x, 2)
Next x
'Step through our second dictionary and check if value < 10
For Each Key In dict2.keys
If dict2(Key) < 10 Then
If Not rng Is Nothing Then
Set rng = Union(rng, .Range(dict1(Key)))
Else
Set rng = .Range(dict1(Key))
End If
End If
Next Key
'If any where below 10, this Range object has been filled, so delete it.
If Not rng Is Nothing Then
rng.Delete
End If
End With
End Sub
Here is another method that uses Autofilter and SUMIF to delete the lines.
This assumes there is a header row, if not then add a row first.
It adds a sumif in column C and filters all that is less than 10, then deletes them.
Then removes column C again.
Sub filter()
Range("C1").Value = "Sum"
Lastrow = Range("A" & Rows.Count).End(xlUp).Row
Range("C2:C" & Lastrow).Formula = "=sumif(A:A,A2,B:B)"
Range("A2").AutoFilter ' add a filter to table
ActiveSheet.Range("$A$1:$C$" & Lastrow).AutoFilter Field:=3, Criteria1:="<10", Operator:=xlAnd ' filter all below 10
ActiveSheet.Range("A2:C" & Lastrow).SpecialCells(xlCellTypeVisible).EntireRow.Delete ' delete them
Range("A1").AutoFilter ' remove filter again
Columns("C:C").EntireColumn.Delete ' remove column C
End Sub
I have a table as shown below,based on yellow highlighted column i need to sum green highlighted columns.
Expected output is here:
I have done it using the below code …
Sub test()
lrow = ActiveSheet.Cells(ActiveSheet.Cells.Rows.Count, 1).End(xlUp).Row
Set Rng = Range("A2:A" & lrow)
For Each cell In Rng
If Not IsEmpty(cell) Then
a = cell
b = cell.Offset(0, 1)
c = cell.Offset(0, 5)
r = cell.Row
cnt = Application.WorksheetFunction.CountIf(Rng, cell)
d = 0
For i = 1 To cnt
If Cells(r + i, 1) = a And Cells(r + i, 2) = b And Cells(r + i, 6) Then
Cells(r, 7) = Cells(r + i, 7) + Cells(r, 7)
Cells(r, 8) = Cells(r + i, 8) + Cells(r, 8)
d = d + 1
End If
Next
If d > 0 Then Range(Cells(r + 1, 1).Address, Cells(r + d, 1).Address).EntireRow.Delete
End If
Next
End Sub
I want to do it using scripting dictionary, which is new for me. Since I'm a beginner, I'm unable to modify the below example code found in net!!
Got it from here
Sub MG02Sep59()
Dim Rng As Range, Dn As Range, n As Long, nRng As Range
Set Rng = Range(Range("A2"), Range("A" & Rows.Count).End(xlUp))
With CreateObject("scripting.dictionary")
.CompareMode = vbTextCompare
For Each Dn In Rng
If Not .Exists(Dn.Value) Then
.Add Dn.Value, Dn
Else
If nRng Is Nothing Then Set nRng = Dn Else Set nRng = Union(nRng, Dn)
.Item(Dn.Value).Offset(, 3) = .Item(Dn.Value).Offset(, 3) + Dn.Offset(, 3)
End If
Next
If Not nRng Is Nothing Then nRng.EntireRow.Delete
End With
End Sub
Can anyone help me out? with some notes if possible.
this is how I would do it:
Option Explicit
Sub Test()
Dim ws As Worksheet
Dim arrData As Variant
Dim i As Long, ConcatenateStr As String, Sum1 As Currency, Sum2 As Currency
Dim DictSum1 As Scripting.Dictionary 'You need the Microsoft Scripting Runtime reference for this to work
Dim DictSum2 As Scripting.Dictionary
Set ws = ThisWorkbook.Sheets("SheetName") 'Change this to fit your sheet name
Set DictSum1 = New Scripting.Dictionary 'This is how you initialize your dictionary
Set DictSum2 = New Scripting.Dictionary
'Store everything on your sheet into the array
arrData = ws.UsedRange.Value 'this will get from A1 till ctrl+end cell I'd delete rows and columns that are blank
'Loop through the array to fill the dictionary
For i = 2 To UBound(arrData) '2 because row 1 are headers, UBound is the function to get the last item of your array like .count
If arrData(i, 1) = vbNullString Then Exit For 'this will end the loop once finding an empty value on column A
ConcatenateStr = arrData(i, 1) & arrData(i, 2) & arrData(i, 3) & arrData(i, 6) 'this is to work cleaner, each number is the number of the column concatenated
Sum1 = arrData(i, 7) 'column Sum 1
Sum2 = arrData(i, 8) 'column Sum 2
If Not DictSum1.Exists(ConcatenateStr) Then 'For the column Sum 1
DictSum1.Add ConcatenateStr, Sum1 'this will add the first item Key = Concatenate String and item = the money value
Else
DictSum1(ConcatenateStr) = DictSum1(ConcatenateStr) + Sum1 'this will sum the existing value on the dictionary + the current value of the loop
End If
If Not DictSum2.Exists(ConcatenateStr) Then 'For the column Sum 2
DictSum2.Add ConcatenateStr, Sum2 'this will add the first item Key = Concatenate String and item = the money value
Else
DictSum2(ConcatenateStr) = DictSum2(ConcatenateStr) + Sum2 'this will sum the existing value on the dictionary + the current value of the loop
End If
Next i
Erase arrData
With ws
.UsedRange.RemoveDuplicates Columns:=Array(1, 2, 3, 6), Header:=xlYes 'Again UsedRange will take everything, Columns as you can see are the ones highlighted in yellow
arrData = .UsedRange.Value 'Store the results of deleting all the duplicates
For i = 2 To UBound(arrData) 'Lets fill the array with the sums
ConcatenateStr = arrData(i, 1) & arrData(i, 2) & arrData(i, 3) & arrData(i, 6)
arrData(i, 8) = DictSum1(ConcatenateStr)
arrData(i, 9) = DictSum2(ConcatenateStr)
Next i
.UsedRange.Value = arrData 'Paste back the array with all the sums
End With
End Sub
I've commented the code, but to learn more about dictionaries check this awesome tutorial
I have created a nested for loop to compare 3 different cell values within 2 sheets. The loop works fine when the data is small, but when I run on 5,000 rows its too slow and crashes excel. Any idea of how to run this more efficiently.
Sub RowMatch()
Dim x As Integer
' Make sure we are in the right sheet
Worksheets("Q416").Activate
' Set numrows = number of rows of data.
NumRows = Range("C2", Range("C2").End(xlDown)).Rows.count
' find the reference range
Worksheets("Q415").Activate
NumRows2 = Range("C5", Range("C5").End(xlDown)).Rows.count
Worksheets("Q416").Activate
MsgBox ("Total # of Rows on this sheet = " & NumRows & " and " & NumRows2 & " in Ref Range")
Range("A1").Select
' Establish "For" loop to loop "numrows" number of times.
For x = 1 To NumRows
'MsgBox NumRows2
For y = 1 To NumRows2
'MsgBox (ActiveCell.Offset(x, 0).Value & " & " & Worksheets("Q415").Cells(y + 1, 1))
If ActiveCell.Offset(x, 0).Value = Worksheets("Q415").Cells(y + 1, 1).Value _
And ActiveCell.Offset(x, 2).Value = Worksheets("Q415").Cells(y + 1, 3).Value Then
If ActiveCell.Offset(x, 5).Value = Worksheets("Q415").Cells(y + 1, 6).Value Then
'If NumRows(i).Value = ActiveCell.Offset(1, 0).Value Then
ActiveCell.Offset(x, 10).Value = "Same"
Else
ActiveCell.Offset(x, 10).Value = ActiveCell.Offset(x, 5).Value - Worksheets("Q415").Cells(y + 1, 6).Value
End If
End If
Next y
Next x
End Sub
Reading and writing to cells is one of the slowest operations you can do in Excel VBA. Instead, you should place the values contained in the worksheets into arrays and work with them there, Here is an excellent reference: http://www.cpearson.com/excel/ArraysAndRanges.aspx. Use your NumRows variables and either a column letter or number to define the ranges that will consitute the arrays e.g:
myRange = Range("A1:C" & NumRows)
myArray = myRange.value
From the link to Chip Pearsons site:
Dim Arr() As Variant
Arr = Range("A1:B10")
Dim R As Long
Dim C As Long
For R = 1 To UBound(Arr, 1) ' First array dimension is rows.
For C = 1 To UBound(Arr, 2) ' Second array dimension is columns.
Debug.Print Arr(R, C)
Next C
Next R