This function returns Range R exclusive of Range Ex, which does not have to be on the same worksheet. It includes three methods that depend on conditions:
- Fast -- use a temporary worksheet (credit Tom Ogilvy and Tushar Mehta)
- Quick -- R and Ex are each filled rectangles and Ex is wholly within R
- Slower -- consider each cell
Function Range_Ex(R As Range, Ex As Range) As Range
'
' Return Range R exclusive of Range Ex; Ex does not have to be on R.Worksheet
' Prefer method (fast): Use a temporary worksheet
' Special case (quick): R and Ex are each filled rectangles and Ex is wholly within R
' Last method (slower): Consider each cell
'
' see http://dailydoseofexcel.com/archives/2007/08/17/two-new-range-functions-union-and-subtract/
'
' Aug 2022 by J. Woolley
'
Dim X As Range, T As Range
Dim bEV As Boolean, bSU As Boolean, bDA As Boolean
Dim nRrows As Long, nRcols As Long, nXrows As Long, nXcols As Long
Dim begXrow As Long, begXcol As Long, endXrow As Long, endXcol As Long
If R Is Nothing Or Ex Is Nothing Then Set Range_Ex = R: Exit Function
If R.Address = Ex.Address Then Set Range_Ex = Nothing: Exit Function
Set X = R.Worksheet.Range(Ex.Address) ' necessary for Intersect and Union
If Application.Intersect(R, X) Is Nothing Then Set Range_Ex = R: Exit Function
With ActiveWorkbook
If Not .ProtectStructure Then ' use a temporary worksheet (fast)
bSU = Application.ScreenUpdating
Application.ScreenUpdating = False
bEV = Application.EnableEvents
Application.EnableEvents = False
With .Sheets.Add(Count:=1, Type:=xlWorksheet)
.Cells.Clear ' just in case
.Range(R.Address).Value = 1
.Range(X.Address).ClearContents
Set Range_Ex = R.Worksheet.Range(.Cells.SpecialCells(xlCellTypeConstants).Address)
bDA = Application.DisplayAlerts
Application.DisplayAlerts = False
.Delete
Application.DisplayAlerts = bDA
End With
Application.EnableEvents = bEV
Application.ScreenUpdating = bSU
Exit Function ' done
End If
End With
' protected workbook; check for special case (quick); otherwise, consider each cell (slower)
If R.Areas.Count = 1 And X.Areas.Count = 1 And Application.Union(R, X).Address = R.Address Then
nRrows = R.Rows.Count ' X is wholly within R (quick)
nRcols = R.Columns.Count
nXrows = X.Rows.Count
nXcols = X.Columns.Count
begXrow = X.Row - R.Row + 1 ' relative to R
begXcol = X.Column - R.Column + 1 ' ditto
endXrow = begXrow + nXrows - 1 ' ditto
endXcol = begXcol + nXcols - 1 ' ditto
If begXcol > 1 Then Set T = R.Cells(1, 1).Resize(nRrows, (begXcol - 1)) Else Set T = Nothing
Set Range_Ex = T
If endXcol < nRcols Then
Set T = R.Cells(1, (endXcol + 1)).Resize(nRrows, (nRcols - endXcol))
GoSub Update
End If
If begXrow > 1 Then
Set T = R.Cells(1, begXcol).Resize((begXrow - 1), nXcols)
GoSub Update
End If
If endXrow < nRrows Then
Set T = R.Cells((endXrow + 1), begXcol).Resize((nRrows - endXrow), nXcols)
GoSub Update
End If
Else
For Each T In R ' consider each cell (slower)
If Application.Intersect(T, X) Is Nothing Then GoSub Update
Next T
End If
Exit Function
Update: ' use GoSub for common statement
If Range_Ex Is Nothing Then Set Range_Ex = T Else Set Range_Ex = Application.Union(Range_Ex, T)
Return
End Function
Range.Difference(Range(A), Range(B))
, unfortunately. We need to cope withUnion
,Intersect
, and other properties and instructions. Show what are your ranges (picture) or what you have tried (code) for further support. – Fragrantrange.difference
. like in your situation- you want to have a)red minus blue
, the other one could have b)blue minus red
, and final third option is 3)not intersected ranges
... did you tried anything, I mean any code? – Fragrantset
in front, like:Set rng = SetDifference(rng, highlightedColumns)
– Fragrant