[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『VBA: 2つのセル範囲で重なっていない範囲の取得』(VBA勉強中)
VBAで2つの範囲に対し、重なっている範囲を取得するにはIntersectを用いればよいかと思いますが、逆に重なっていない範囲を取得するにはどういう方法が考えられるでしょうか?
具体的には以下のような状況を想定しております。
rng1 = Range("A1:C5")
rng2 = Range("C1:D5")
上記のとき、rng1とrng2の重なっていないRange("A1:B5")およびRange("D1:D5")を取得したいです。
よろしくお願いします。
< 使用 Excel:Excel2019、使用 OS:Windows10 >
かぶってますが...Union かこんな意味ですか?
Dim MyRNG As Range
Set MyRNG = Range("A1:B5,D1:D5")
If Intersect(Target, MyRNG) Is Nothing Then Exit Sub (あみな) 2022/05/16(月) 13:11
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1191798316
(わからん) 2022/05/16(月) 13:19
わからん様
参考になるページを教えていただきありがとうございます!
一通り調べてみたつもりでしたが、お教えいただいたページにはたどり着けておりませんでした。。
引き続き勉強を進めていきます。ありがとうございました。
(VBA勉強中) 2022/05/16(月) 13:39
一時的に作業用ワークシートを追加したらどうでしょう
Sub sample()
Dim rng1 As Range, rng2 As Range
Set rng1 = Range("A1:C5")
Set rng2 = Range("C1:D5")
SymmetricDiff(rng1, rng2).Select
End Sub
Function SymmetricDiff(ByVal r1 As Range, ByVal r2 As Range) As Range
Dim uRng As Range, iRng As Range
Set uRng = Union(r1, r2)
Set iRng = Intersect(r1, r2)
If iRng Is Nothing Then
Set SymmetricDiff = uRng
Exit Function
End If
Set tmpws = Worksheets.Add
Set uRng = tmpws.Range(uRng.Address)
Set iRng = tmpws.Range(iRng.Address)
uRng.Value = 1
iRng.ClearContents
Set SymmetricDiff = uRng.SpecialCells(xlCellTypeConstants)
Set SymmetricDiff = r1.Parent.Range(SymmetricDiff.Address)
Application.DisplayAlerts = False
tmpws.Delete
Application.DisplayAlerts = True
End Function
(´・ω・`) 2022/05/16(月) 13:49
勉強のため皆さまにいただいた例をそれぞれ実装して差を比較したいと思います。
ありがとうございました。
(VBA勉強中) 2022/05/16(月) 14:19
エヘヘ。遅れ馳せながら...^^;
リンク先にも感化され、真正面から取り組んだらどうなるかちょっと考えてみたくなりました。 「Union、Intersectがあって、SQLみたく『Minus』が無いから作ってみよう」ってお題で暇つぶし。
式
Function Minus(Arg1 As Range, Arg2 As Range) As Range
機能
Arg1にあってArg2に無い範囲を返す => Arg1からArg2を除く範囲を返す
方針 UnionやIntersectの様な[Arg3]〜[Arg30]部分はMinus処理には不要だと思われる。 但し、Arg1、Arg2共に複数Areaから成るRangeにも対応させる。 各引数内の各Areaを「矩形オブジェクト」と考え、そのコレクションとして扱って計算してみようと思う。
'[clsRect]モジュール(クラスモジュール)-------------------------------------------------------------
Option Explicit
Public Left As Long
Public Top As Long
Public Right As Long
Public Bottom As Long
Public Property Get ToAddress(Optional ByVal RefStyle As XlReferenceStyle = xlA1) As String
Rem デバッグ用プロパティ(要らん)
Dim a As String
a = "R" & Top & "C" & Left & ":R" & Bottom & "C" & Right
If RefStyle <> xlA1 Then
ToAddress = Application.ConvertFormula(a, xlR1C1)
Else
ToAddress = Application.ConvertFormula(a, xlR1C1, xlA1, xlRelative)
End If
End Property
'以下、標準モジュールの話--------------------------------------------------------------------------
Function Minus(Arg1 As Range, Arg2 As Range) As Range
Rem セル範囲Arg1からセル範囲Arg2を除くセル範囲を返す
If Arg1.Worksheet Is Arg2.Worksheet Then
Dim r As Collection
Set r = MinusRects(Range2Rect(Arg1), Range2Rect(Arg2))
If r.Count > 0 Then Set Minus = Rect2Range(r, Arg1.Worksheet)
Else
Set Minus = Arg1
End If
End Function
Private Function Range2Rect(Arg1 As Range) As Collection
Rem セル範囲Arg1を矩形集合に変換して返す
Dim r As clsRect, a As Range, cRects As New Collection
For Each a In Arg1.Areas
Set r = New clsRect
r.Top = a.Row
r.Left = a.Column
r.Bottom = r.Top + a.Rows.Count - 1
r.Right = r.Left + a.Columns.Count - 1
cRects.Add r
Next
Set Range2Rect = cRects
End Function
Private Function Rect2Range(Arg1 As Collection, Optional ByVal ParentSheet As Worksheet) As Range
Rem 矩形集合Arg1をセル範囲に変換して返す
Dim r As Range, i As Long, e As Long
If ParentSheet Is Nothing Then Set ParentSheet = ActiveSheet
Debug.Print "Rect2Range result --------------------"
For i = 1 To Arg1.Count
On Error Resume Next
With ParentSheet
Set r = Range(.Cells(Arg1.Item(i).Top, Arg1.Item(i).Left), .Cells(Arg1.Item(i).Bottom, Arg1.Item(i).Right))
End With
e = Err.Number
On Error GoTo 0
If e = 0 Then
If Rect2Range Is Nothing Then Set Rect2Range = r Else Set Rect2Range = Union(Rect2Range, r)
End If
Debug.Print i; Arg1.Item(i).ToAddress; " => ";
If Not Rect2Range Is Nothing Then Debug.Print Rect2Range.Address(0, 0);
Debug.Print
Next
End Function
Private Function MinusRects(Rects1 As Collection, Rects2 As Collection) As Collection
Rem 矩形集合1内の各矩形から矩形集合2内の各矩形を除いた矩形集合を作って返す
Dim r As Collection, i As Long
Set r = New Collection
For i = 1 To Rects1.Count
r.Add Rects1.Item(i)
Next
For i = 1 To Rects2.Count
Debug.Print "Minus ["; Rects2.Item(i).ToAddress; "] ---------------------"
Set r = MinusRect_RSmR(r, Rects2.Item(i))
Next
Set MinusRects = r
End Function
Private Function MinusRect_RSmR(Rects1 As Collection, RectB As clsRect) As Collection
Rem 中継関数
Dim cRtn As Collection, i As Long
Dim iRect As clsRect
Dim cRects As New Collection, j As Long
For i = 1 To Rects1.Count
Set iRect = Rects1.Item(i)
Debug.Print i, iRect.ToAddress; " - "; RectB.ToAddress
Set cRtn = MinusRect(iRect, RectB)
For j = 1 To cRtn.Count
Debug.Print , j, cRtn.Item(j).ToAddress
cRects.Add cRtn.Item(j)
Next
Next
Set MinusRect_RSmR = cRects
End Function
Private Function MinusRect(RectA As clsRect, RectB As clsRect) As Collection
Rem 矩形Aから矩形Bを除いた矩形集合(行優先)を作って返す
Dim v As Collection, h As Collection, i As Long, j As Long
Dim r As clsRect, cRects As New Collection
Set v = LineDivider(RectA.Top, RectA.Bottom, RectB.Top, RectB.Bottom)
Set h = LineDivider(RectA.Left, RectA.Right, RectB.Left, RectB.Right)
For i = 1 To v.Count
If v(i)(2) Then
For j = 1 To h.Count
If Not h(j)(2) Then
Set r = New clsRect
r.Top = v(i)(0)
r.Bottom = v(i)(1)
r.Left = h(j)(0)
r.Right = h(j)(1)
cRects.Add r
End If
Next
Else
Set r = New clsRect
r.Top = v(i)(0)
r.Bottom = v(i)(1)
r.Left = RectA.Left
r.Right = RectA.Right
cRects.Add r
End If
Next
Set MinusRect = cRects
End Function
Private Function LineDivider(minA As Long, maxA As Long, minB As Long, maxB As Long) As Collection
Rem 区間Aを区間Bと重なる部分と重ならない部分に分割(配列(始点Long,終点Long,重なりBool)のコレクション)
Dim c As New Collection, Ary(0 To 2)
If maxB < minA Or minB > maxA Then '<---------重ならない
Ary(0) = minA: Ary(1) = maxA
Ary(2) = False
c.Add Ary
ElseIf minB <= minA And maxB >= maxA Then '<--Aの全区間が重なる
Ary(0) = minA: Ary(1) = maxA
Ary(2) = True
c.Add Ary
ElseIf minB > minA And maxB < maxA Then '<----Bの全区間が重なる
Ary(0) = minA: Ary(1) = minB - 1
Ary(2) = False
c.Add Ary
Ary(0) = minB: Ary(1) = maxB
Ary(2) = True
c.Add Ary
Ary(0) = maxB + 1: Ary(1) = maxA
Ary(2) = False
c.Add Ary
ElseIf maxB < maxA Then '<--------------------Aの途中まで重なる
Ary(0) = minA: Ary(1) = maxB
Ary(2) = True
c.Add Ary
Ary(0) = maxB + 1: Ary(1) = maxA
Ary(2) = False
c.Add Ary
ElseIf minB > minA Then '<--------------------Aの途中から重なる
Ary(0) = minA: Ary(1) = minB - 1
Ary(2) = False
c.Add Ary
Ary(0) = minB: Ary(1) = maxA
Ary(2) = True
c.Add Ary
End If
Set LineDivider = c
End Function
'Minusメソッド使用例 ------------------------------------------------------------------------------
Sub 選択範囲の除去()
If ActiveWorkbook Is Nothing Then Exit Sub
If TypeName(Selection) <> "Range" Then Exit Sub
Dim Ra As Range
On Error Resume Next
Set Ra = Application.InputBox("現在の選択範囲と交わる範囲を選択して下さい。", Title:="除去する範囲の選択", Type:=8)
On Error GoTo 0
If Ra Is Nothing Then Exit Sub
Set Ra = Minus(ActiveWindow.RangeSelection, Ra)
If Ra Is Nothing Then
MsgBox "選択範囲が全滅します。", vbCritical
Exit Sub
End If
Ra.Select
End Sub
Sub 動作確認()
Dim Ra As Range, Rb As Range
Set Ra = [A4:G6,B1:F9]
Ra.Select
Set Rb = [C2:D2,C5:D7]
Debug.Print "▼["; Ra.Address(0, 0); "] - ["; Rb.Address(0, 0); "]"
Set Ra = Minus(Ra, Rb)
Debug.Print "■ => ["; Ra.Address(0, 0); "]"
Ra.Select
End Sub
Sub 重なっていない範囲の取得テスト()
Dim Ra As Range, Rb As Range, Rc As Range
Set Ra = [A1:C5]
Set Rb = [C1:D5]
Set Rc = Minus(Union(Ra, Rb), Intersect(Ra, Rb))
Debug.Print Rc.Address(0, 0)
End Sub
(白茶) 2022/05/20(金) 20:15
あ。 もうひとつ使い道思い付いたのでメモ ^^;
Minus(Cells, Minus(Cells, 範囲)) で、 順番や重複がぐちゃぐちゃした範囲をキレイに整えられますね。
例
[B5:F5,D5:D19,B9:B19,F9:F19,B13:F14,B5:F6]
↓
[B5:F6,B9:B12,D7:D12,F9:F12,B13:F14,B15:B19,D15:D19,F15:F19]
(白茶) 2022/05/21(土) 17:59
Function RangeN(領域 As Range) As Dictionary
Dim dic As New Dictionary
Dim WA As Range
Dim i As Long
Dim W1 As Variant
Dim wK As Variant
For Each WA In 領域.Areas
For Each W1 In WA.Cells
Select Case dic.Exists(W1.Address)
Case True: dic(W1.Address) = Empty
Case Else: dic.Add W1.Address, W1
End Select
Next
Next
For Each wK In dic.Keys
If IsEmpty(dic(wK)) Then dic.Remove wK
Next
Set RangeN = dic
End Function
Sub test()
Dim x As Variant
Dim W9 As Range
Dim 範囲 As Range
' Set 範囲 = Range("A1:C5,C1:D5,A2:E3")
Set 範囲 = Range("A1:C5,C1:D5")
'(1) --各要素へのアクセスだけなら ↓ な感じで
For Each x In RangeN(範囲).Items
Debug.Print x.Address; x.Value
Next
'(2)敢えてRangeがほしいなら
For Each x In RangeN(範囲).Items
If W9 Is Nothing Then Set W9 = x Else Set W9 = Union(W9, x)
Next
Debug.Print W9.Address
End Sub
(チオチモリン) 2022/05/21(土) 23:22
>rng1 = Range("A1:C5")
>rng2 = Range("C1:D5")
>上記のとき、rng1とrng2の重なっていないRange("A1:B5")およびRange("D1:D5")を取得したいです。
なら ↓ のようでしょうか
Sub test2()
Dim rng1 As Range
Dim rng2 As Range
Dim W9 As Range
Set rng1 = Range("A1:C5")
Set rng2 = Range("C1:D5")
MsgBox RangeN2(rng1, rng2).Address
MsgBox RangeN2(rng1, rng2, Range("A2:E3")).Address
End Sub
Function RangeN2(ParamArray 領域() As Variant) As Range
Dim dic As New Dictionary
Dim i As Long
Dim W1 As Variant
Dim wK As Variant
Dim W9 As Range
For i = 0 To UBound(領域)
For Each W1 In 領域(i).Cells
Select Case dic.Exists(W1.Address)
Case True: dic(W1.Address) = Empty
Case Else: dic.Add W1.Address, W1
End Select
Next
Next
For Each wK In dic.Keys
If IsEmpty(dic(wK)) = False Then
If W9 Is Nothing Then Set W9 = dic(wK) Else Set W9 = Union(W9, dic(wK))
End If
Next
Set RangeN2 = W9
End Function
(チオチモリン) 2022/05/23(月) 09:33
(独り言です。スルー希望 ^^; )
なんか神髄さんで似たような話題を見つけてしまった...
Rangeオブジェクトの論理演算(差集合と排他的論理和)|VBA技術解説
https://excel-ubara.com/excelvba4/EXCEL_VBA_425.html
Union 和集合 Intersect 積集合 Except 差集合 (今回の「Minus」) Complement 補集合 (「Not(範囲)」的な...)
Except([A], [B]) = Intersect([A], Complement([B])) なので 「Complementを作ってみよう」で話を進めても良かったかも知れない。 (引数範囲の上下左右に存在する矩形集合を返す。という)
でも Complement([B]) = Except(Cells, [B]) だから Complementを作る作業って結局Exceptを作る作業とほとんど概念が同じなのね。
やっぱ「Exceptを作ってみよう」で良かったんだろう。(としておこう)
* * *
しかしComplementも案外使えるかもなぁ... 単独で使用する機能ではないだろうけど、 PhotoShop等の画像処理系では「選択範囲の反転」機能は必須で、 例えば[反転]→[調整]→[反転]という流れは常套手段だし。
「○○なセル範囲」を取得する機能を持っていれば、 「○○以外なセル範囲」を取得する機能も間接的に実装出来ちゃう訳で。 (例:空白セル以外とか、数式セル以外、この色以外とか...) ちょっと試験配備ですな
(白茶) 2022/05/26(木) 10:57
重なった所の文字消す。
です。
(甲樹) 2022/05/26(木) 17:03
>これじゃダメ? もちろんアリだと思います。 (´・ω・`)さんの 2022/05/16(月) 13:49 の様な手法ですよね。
別に否定してる訳じゃなくって、 何ていうか、もう少しオタクっぽい話なんすね。
範囲選択の話なんだから「何かの最中」に必要となるであろう機能なのにも関わらず、 ただ範囲選択するだけでUnDoリスト破棄されたり裏で無駄なイベントが投げ込まれたりされちゃう手法を 自ら選ぶ気には到底なれない。とか ましてそんな機能を自分が使うアドインに組み込むなんで我慢ならない。とか...
これに関しては汎用化を想定するにあたって正攻法を採らないと気が済まなかったんですね。 (実際使う時にはホントどうでもいいことなんですけどね!!) ま、めんどくせー奴なんすよ^^;
# 解決後にウダウダとしつこく書かせて頂いた身として責任を感じたのでスルー出来ませんでした。 # 「別にあんたに訊いてないよ」って場合はご容赦下さい。
(白茶) 2022/05/26(木) 19:06
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.