Jaafar Tribak
Well-known Member
- Joined
- Dec 5, 2002
- Messages
- 9,797
- Office Version
- 2016
- Platform
- Windows
Hi all,
As shown here : Subtracting ranges in VBA (Excel)
I'm trying to write a function to subtract Excel ranges. It should take two input parameters: range A and range B. It should return a range object consisting of cells that are part of range A and are not part of range B.
I am using D_ick Kusleika's recursive function which is really neat and fast but when range B becomes larger, the function takes for ever.
Here is the func from the link above:
Testing:
Now, as you start increasing the size of the B range, the func becomes slower and slower :
Set B = .Range(.Cells(2, 2), .Cells(100, 100)) '<== Takes 1 secs
Set B = .Range(.Cells(2, 2), .Cells(500, 500)) '<== Takes 9 secs
Set B = .Range(.Cells(2, 2), .Cells(1000, 1000)) '<== Takes 34 secs
I wonder if there is a faster alternative. Thank you.
As shown here : Subtracting ranges in VBA (Excel)
I'm trying to write a function to subtract Excel ranges. It should take two input parameters: range A and range B. It should return a range object consisting of cells that are part of range A and are not part of range B.
I am using D_ick Kusleika's recursive function which is really neat and fast but when range B becomes larger, the function takes for ever.
Here is the func from the link above:
VBA Code:
Private mrBuild As Range
Public Function SubtractRanges(rFirst As Range, rSecond As Range) As Range
Dim rInter As Range
Dim rReturn As Range
Dim rArea As Range
Set rInter = Intersect(rFirst, rSecond)
Set mrBuild = Nothing
If rInter Is Nothing Then 'No overlap
Set rReturn = rFirst
ElseIf rInter.Address = rFirst.Address Then 'total overlap
Set rReturn = Nothing
Else 'partial overlap
For Each rArea In rFirst.Areas
BuildRange rArea, rInter
Next rArea
Set rReturn = mrBuild
End If
Set SubtractRanges = rReturn
End Function
Sub BuildRange(rArea As Range, rInter As Range)
Dim rLeft As Range, rRight As Range
Dim rTop As Range, rBottom As Range
If Intersect(rArea, rInter) Is Nothing Then 'no overlap
If mrBuild Is Nothing Then
Set mrBuild = rArea
Else
Set mrBuild = Union(mrBuild, rArea)
End If
Else 'some overlap
If rArea.Columns.Count = 1 Then 'we've exhausted columns, so split on rows
If rArea.Rows.Count > 1 Then 'if one cell left, don't do anything
Set rTop = rArea.Resize(rArea.Rows.Count \ 2) 'split the range top to bottom
Set rBottom = rArea.Resize(rArea.Rows.Count - rTop.Rows.Count).Offset(rTop.Rows.Count)
BuildRange rTop, rInter 'rerun it
BuildRange rBottom, rInter
End If
Else
Set rLeft = rArea.Resize(, rArea.Columns.Count \ 2) 'split the range left to right
Set rRight = rArea.Resize(, rArea.Columns.Count - rLeft.Columns.Count).Offset(, rLeft.Columns.Count)
BuildRange rLeft, rInter 'rerun it
BuildRange rRight, rInter
End If
End If
End Sub
Testing:
VBA Code:
Sub Test()
Dim A As Range, B As Range, R As Range
Dim sngStartTimer As Single
sngStartTimer = Timer
With Sheet1
Set A = .Cells
Set B = .Range(.Cells(2, 2), .Cells(100, 100))
End With
Set R = SubtractRanges(A, B)
If Not R Is Nothing Then R.Select
MsgBox Timer - sngStartTimer
End Sub
Now, as you start increasing the size of the B range, the func becomes slower and slower :
Set B = .Range(.Cells(2, 2), .Cells(100, 100)) '<== Takes 1 secs
Set B = .Range(.Cells(2, 2), .Cells(500, 500)) '<== Takes 9 secs
Set B = .Range(.Cells(2, 2), .Cells(1000, 1000)) '<== Takes 34 secs
I wonder if there is a faster alternative. Thank you.