仕様。何ができるクラスなの?
- 検索文字列は複数渡すことができる。
- 検索は完全一致、部分一致の両方に対応する。
- 結果は A1 参照形式、R1C1 参照形式のどちらでも得られる。例えばこんなイメージ。
- A1 参照形式: (“A1”, “B3”, “C6”)
- R1C1 参照形式: ((1,1), (2,3), (3,6))
VBA コード
''' <summary>
''' 指定ブックのワークシートの範囲から文字列が一致する場所を返すクラスです。
''' </summary>
Option Explicit
Private mvntValues As Variant
Private mvntRangeLeftUpIndexes As Variant
Private mcolSearchWords As Collection
' 2 次元配列を検索してヒットした要素のインデックス
' (Array(1 次元目インデックス, 2 次元目インデックス))
' を格納したコレクション
Private mcolHitIndexes As Collection
''' <summary>
''' 初期化処理を実行します。
''' </summary>
''' <param name="strBook">対象ブック名</param>
''' <param name="strSheet">対象シート名</param>
''' <param name="strRange">対象範囲名</param>
''' <param name="vntWords">検索文字列配列</param>
Public Sub Init( _
ByVal strBook As String, _
ByVal strSheet As String, _
ByVal strRange As String, _
ByVal vntWords As Variant)
' 変数をセット
Dim r As Range: Set r = Workbooks(strBook).Sheets(strSheet).range(strRange)
mvntValues = r.Value
mvntRangeLeftUpIndexes = GetLUArray(r)
Set mcolSearchWords = New Collection
Dim word As Variant
For Each word In vntWords
Call mcolSearchWords.Add(word)
Next word
End Sub
''' <summary>
''' Range の左上のセルの行番号、列番号を返します。
''' </summary>
''' <param name="r">Range オブジェクト</param>
''' <returns>
''' インデックス 0 要素に Range 左上セルの行番号を格納し、
''' インデックス 1 要素に Range 左上セルの列番号を格納した配列
'''</returns>
Private Function GetLUArray(ByVal r As Range) As Variant
' Range の左上のセルアドレスを R1C1 参照形式で取得
Dim address As String: address = Application.ConvertFormula(r.Item(1).address, xlA1, xlR1C1)
Dim lngRPosition As Long: lngRPosition = InStr(address, "R")
Dim lngCPosition As Long: lngCPosition = InStr(address, "C")
' "R" と "C" の間が行番号
Dim rowNum As Long: rowNum = Mid(address, lngRPosition + 1, lngCPosition - lngRPosition -1)
' "C" より右が列番号
Dim colNum As Long: colNum = Right(address, Len(address) - lngCPosition)
GetLUArray = Array(rowNum, colNum)
End Function
''' <summary>
''' Range を検索し、検索文字列に一致する内容を格納したセルを探します。
''' </summary>
Public Sub Search()
Set mcolHitIndexes = SearchByWords(mcolSearchWords, mvntValues)
End Sub
''' <summary>
''' Range を検索し、検索文字列に一致する内容を格納したセルを探します。
''' *検索文字列* の Like 検索を行います。
''' </summary>
Public Sub SearchWithLike()
' 検索対象文字列の最初と最後に * を付加
Dim editedWords As Collection: Set editedWords = New Collection
Dim word As Variant
For Each word In mcolSearchWords
Call editedWords.Add("*" & word & "*")
Next word
Set mcolHitIndexes = SearchByWords(editedWords, mvntValues)
End Sub
''' <summary>
''' 文字列が一致する場所を格納したコレクションを返します。
''' </summary>
''' <param name="words">検索文字列コレクション</param>
''' <param name="values">検索対象の 2 次元配列</param>
''' <returns>
''' インデックス 0 要素にヒットした引数配列の 1 次元目インデックスを格納し、
''' インデックス 1 要素にヒットした引数配列の 2 次元目インデックスを格納した配列を
''' 要素としたコレクション
'''</returns>
Private Function SearchByWords( _
ByVal words As Collection, _
ByVal values As Variant) As Collection
Dim colHitIndexes As Collection: Set colHitIndexes = New Collection
Dim word As Variant
Dim hits As Collection
Dim hit As Variant
For Each word In words
Set hits = SearchByWord(word, values)
For Each hit in hits
Call colHitIndexes.Add(hit)
Next hit
Next word
Set SearchByWords = colHitIndexes
End Function
''' <summary>
''' 文字列が一致する場所を格納したコレクションを返します。
''' </summary>
''' <param name="word">検索文字列</param>
''' <param name="values">検索対象の 2 次元配列</param>
''' <returns>
''' インデックス 0 要素にヒットした引数配列の 1 次元目インデックスを格納し、
''' インデックス 1 要素にヒットした引数配列の 2 次元目インデックスを格納した配列を
''' 要素としたコレクション
'''</returns>
Private Function SearchByWord( _
ByVal word As Variant, _
ByVal values As Variant) As Collection
Dim hits As Collection: Set hits = New Collection
Dim i As Long, j As Long
For i = LBound(values, 1) To UBound(values, 1)
For j = LBound(values, 2) To UBound(values, 2)
If values(i, j) Like word Then
Call hits.Add(Array(i, j))
End If
Next j
Next i
Set SearchByWord = hits
End Function
''' <summary>
''' 検索文字列に一致するセルの場所を返します。
''' </summary>
''' <returns>
''' インデックス 0 要素にヒットしたセルの行番号を格納し、
''' インデックス 1 要素にヒットしたセルの列番号を格納した配列を
''' 要素としたコレクション
'''</returns>
Public Function GetR1C1Locations() As Collection
Dim row As Long
Dim col As Long
Dim locations As Collection: Set locations = New Collection
Dim index As Variant
For Each index In mcolHitIndexes
' Range 左上セル座標に 2 次元配列のヒットした要素のインデックスを加えて、
' ワークシートでのヒットしたセル位置を決定
row = mvntRangeLeftUpIndexes(0) + index(0) - 1
col = mvntRangeLeftUpIndexes(1) + index(1) - 1
Call locations.Add(Array(row, col))
Next index
Set GetR1C1Locations = locations
End Function
''' <summary>
''' 検索文字列に一致するセルの場所を返します。
''' </summary>
''' <returns>A1 参照形式で表現した座標文字列のコレクション</returns>
Public Function GetA1Locations() As Collection
Dim before As Collection: Set before = GetR1C1Locations
Dim after As Collection: Set after = New Collection
Dim r1c1 As Variant
For Each r1c1 In before
' R1C1 参照形式を A1 参照形式へ変換
Call after.Add(Cells(r1c1(0), r1c1(1)).address(False, False))
Next r1c1
Set GetA1Locations = after
End Function
確認用のテストコードです。
Option Explicit
' LocationSearcher.Search のテスト
Public Sub Test()
' 定義
Dim b As String: b = "Book1.xlsm"
Dim s As String: s = "Sheet1"
Dim r As String: r = "B2:F6"
' 検索ワード
Dim words As Variant: words = Array("cc", "d3")
' クラス生成・実行
Dim ls As LocationSearcher: Set ls = New LocationSearcher
Call ls.Init(b, s, r, words)
Call ls.Search
Dim r1c1s As Collection: Set r1c1s = ls.GetR1C1Locations
Dim a1s As Collection: Set a1s = ls.GetA1Locations
' 確認
Dim i As Long
Dim result As String
Dim expectR1c1s As Variant: expectR1c1s = Array(Array(3, 3), Array(4,3), Array(3, 4))
For i = 1 To r1c1s.Count
result = "予想: " & expectR1c1s(i - 1)(0) & expectR1c1s(i - 1)(1) & ". 結果: " & r1c1s(i)(0) & r1c1s(i)(1)
If expectR1c1s(i - 1)(0) = r1c1s(i)(0) And expectR1c1s(i - 1)(1) = r1c1s(i)(1) Then
Debug.Print "OK. " & result
Else
Debug.Print "NG. " & result
End If
Next i
Dim expectA1s As Variant: expectA1s = Array("C3", "C4", "D3")
For i = 1 To a1s.Count
result = "予想: " & expectA1s(i -1) & ". 結果: " & a1s(i)
If expectA1s(i -1) = a1s(i) Then
Debug.Print "OK. " & result
Else
Debug.Print "NG. " & result
End If
Next i
End Sub
' LocationSearcher.SearchWithLike のテスト
Public Sub Test2()
' 定義
Dim b As String: b = "Book1.xlsm"
Dim s As String: s = "Sheet1"
Dim r As String: r = "B2:F6"
' 検索ワード
Dim words As Variant: words = Array("c", "d")
' クラス生成・実行
Dim ls As LocationSearcher: Set ls = New LocationSearcher
Call ls.Init(b, s, r, words)
Call ls.SearchWithLike
Dim r1c1s As Collection: Set r1c1s = ls.GetR1C1Locations
Dim a1s As Collection: Set a1s = ls.GetA1Locations
' 確認
Dim i As Long
Dim result As String
Dim expectR1c1s As Variant: expectR1c1s = Array(Array(3, 3), Array(4,3), Array(5, 3), Array(3, 4), Array(4,4), Array(5, 4))
For i = 1 To r1c1s.Count
result = "予想: " & expectR1c1s(i - 1)(0) & expectR1c1s(i - 1)(1) & ". 結果: " & r1c1s(i)(0) & r1c1s(i)(1)
If expectR1c1s(i - 1)(0) = r1c1s(i)(0) And expectR1c1s(i - 1)(1) = r1c1s(i)(1) Then
Debug.Print "OK. " & result
Else
Debug.Print "NG. " & result
End If
Next i
Dim expectA1s As Variant: expectA1s = Array("C3", "C4", "C5", "D3", "D4", "D5")
For i = 1 To a1s.Count
result = "予想: " & expectA1s(i -1) & ". 結果: " & a1s(i)
If expectA1s(i -1) = a1s(i) Then
Debug.Print "OK. " & result
Else
Debug.Print "NG. " & result
End If
Next i
End Sub
そして、確認用エクセルワークシートです。
- [Book1.xlsm]Sheet1
- C3 セルを起点に下記を入力
| cc | d3 | e3 |
| cc | d4 | e4 |
| c5 | d5 | e5 |
おまけ。却下したやり方
WorksheetFunction.Match 関数概要
検索するのに WorksheetFunction.Match 関数は適さなかった。2 次元の Range を検索範囲とするとヒットしなかった。
WorksheetFunction.Match 関数の特徴と確認コード
Match 関数の覚えておきたいと感じた特徴です。
- Match 関数は英字の大文字と小文字は区別しない。
確認用のエクセルワークシートは上述したものと同じです。
Public Sub Test()
Dim b As Workbook: Set b = Workbooks("Book1.xlsm")
Dim s As Worksheet: Set s = b.Worksheets("Sheet1")
Dim r As Range: Set r = s.Range("B2:F6")
Dim result As Variant: result = Application.Match("cc", r, 0)
If IsError(result) Then
Debug.Print "なし"
Else
Debug.Print result
End If
End Sub
おわりに
参考ページです。ありがとう存じます!
- Range の左上セル座標を知るためのヒントになった。
- A1 参照形式から R1C1 参照形式へ変換する方法を知れた。
- R1C1 参照形式の座標文字列から行番号を取り出す方法を知れた。
- LIKE による部分一致の書き方を確認できた。
- Cells.Adress の使い方を確認できた。
以上です。
