Union, Intersect そして Except
セルの 和集合を作成する Application.Union と
積集合を作成する Application.Intersect っていうメソッドがあるのに、
差集合を作成するメソッドはありません。
Google で探しても見つからなかったので、
作ってみましたがいまいち用途がわかりません。だから無いのか?
やり方としては、
A - B = A ∩ ¬ B
というように、引くほうのセル範囲を反転させてから Intersect しています。
素直に引き算しようとすると難しく、この方法にたどり着くのに結構時間がかかりました。
ついでに、Application.Union と Application.Intersect で
Find とか SpecialCells とかの結果をほいほい突っ込めるように
Nothing を渡しても大丈夫な関数も作りました。
関数名考える気力が無かったので Union2 とかアホな名前になっています。
' 和集合 ' Union2(ParamArray ArgList() As Variant) As Range ' ' 積集合 ' Intersect2(ParamArray ArgList() As Variant) As Range ' ' 差集合 ' Except2(ByRef SourceRange As Variant, ParamArray ArgList() As Variant) As Range ' ' セル範囲の反転 ' Invert2(ByRef SourceRange As Variant) As Range ' ' '# 複数のセル ArgList の和集合を返す '# Application.Union の拡張版 Nothing でもOK Public Function Union2(ParamArray ArgList() As Variant) As Range Dim buf As Range Dim i As Long For i = 0 To UBound(ArgList) If TypeName(ArgList(i)) = "Range" Then If buf Is Nothing Then Set buf = ArgList(i) Else Set buf = Application.Union(buf, ArgList(i)) End If End If Next Set Union2 = buf End Function '# 複数のセル ArgList の積集合を返す '# Application.Intersect の拡張版 Nothing でもOK Public Function Intersect2(ParamArray ArgList() As Variant) As Range Dim buf As Range Dim i As Long For i = 0 To UBound(ArgList) If Not TypeName(ArgList(i)) = "Range" Then Exit Function ElseIf buf Is Nothing Then Set buf = ArgList(i) Else Set buf = Application.Intersect(buf, ArgList(i)) End If If buf Is Nothing Then Exit Function Next Set Intersect2 = buf End Function '# SourceRange から ArgList を差し引いた差集合を返す '# (SourceRange と 反転した ArgList との積集合を返す) Public Function Except2 _ (ByRef SourceRange As Variant, ParamArray ArgList() As Variant) As Range If TypeName(SourceRange) = "Range" Then Dim buf As Range Set buf = SourceRange Dim i As Long For i = 0 To UBound(ArgList) If TypeName(ArgList(i)) = "Range" Then Set buf = Intersect2(buf, Invert2(ArgList(i))) End If Next Set Except2 = buf End If End Function '# SourceRange の選択範囲を反転する Public Function Invert2(ByRef SourceRange As Variant) As Range If Not TypeName(SourceRange) = "Range" Then Exit Function Dim sh As Worksheet Set sh = SourceRange.Parent Dim buf As Range Set buf = SourceRange.Parent.Cells Dim a As Range For Each a In SourceRange.Areas Dim AreaTop As Long Dim AreaBottom As Long Dim AreaLeft As Long Dim AreaRight As Long AreaTop = a.Row AreaBottom = AreaTop + a.Rows.Count - 1 AreaLeft = a.Column AreaRight = AreaLeft + a.Columns.Count - 1 '■□□ '■×□ '■□□ ■の部分 Dim RangeLeft As Range Set RangeLeft = GetRangeWithPosition(sh, _ sh.Cells.Row, sh.Cells.Column, sh.Rows.Count, AreaLeft - 1 ) ' Top Left Bottom Right '□□■ '□×■ '□□■ ■の部分 Dim RangeRight As Range Set RangeRight = GetRangeWithPosition(sh, _ sh.Cells.Row, AreaRight + 1, sh.Rows.Count, sh.Columns.Count ) ' Top Left Bottom Right '□■□ '□×□ '□□□ ■の部分 Dim RangeTop As Range Set RangeTop = GetRangeWithPosition(sh, _ sh.Cells.Row, AreaLeft, AreaTop - 1, AreaRight ) ' Top Left Bottom Right '□□□ '□×□ '□■□ ■の部分 Dim RangeBottom As Range Set RangeBottom = GetRangeWithPosition(sh, _ AreaBottom + 1, AreaLeft, sh.Rows.Count, AreaRight ) ' Top Left Bottom Right Set buf = Intersect2(buf, _ Union2(RangeLeft, RangeRight, RangeTop, RangeBottom)) Next Set Invert2 = buf End Function '# 四隅を指定して Range を得る Private Function GetRangeWithPosition(ByRef sh As Worksheet, _ ByVal Top As Long, ByVal Left As Long, _ ByVal Bottom As Long, ByVal Right As Long) As Range '# 無効条件 If Top > Bottom Or Left > Right Then Exit Function ElseIf Top < 0 Or Left < 0 Then Exit Function ElseIf Bottom > Cells.Rows.Count Or Right > Cells.Columns.Count Then Exit Function End If Set GetRangeWithPosition _ = sh.Range(sh.Cells(Top, Left), sh.Cells(Bottom, Right)) End Function