今回は不連続なセル範囲の文字列の結合をしていきたいと思います。
連続する文字列、特定のセル範囲が決まっていてそれを結合するという、ユーザ定義関数は以前作ったことがありますが、
今回は連続している部分もあり、していない部分もあります。
そして、セルの数も不揃いだというような、そういったケースでの結合を考えていきたいと思います。
今回使う、ParamArrayという名前から、配列の一種だと気づく方も多いかと思います。
配列を本格的に学ぶ準備体操としてparamarrayを学んでみましょう。
動画でExcel 引数が不定でもParamArrayで大丈夫、ParamArrayでユーザーファンクション
ParamArray 引数名() As VariantRange なのか単一値なのかを TypeName で判定して処理を分けるテクニックを身につけるignore_empty フラグと IsEmpty 関数を組み合わせて、「空白セルを無視する/しない」を切り替えられるロジックを作る方法を学ぶDim newArray() As Variant を ReDim Preserve で都度拡張しながら値を追加していく実践的な配列操作パターンを習得するJoin(newArray, delimiter) で配列を 1 つの文字列に結合し、ユーザー定義関数の戻り値として返す一連の流れを理解する動画版「マクロ講座」です。
セル範囲の文字列を結合するユーザー定義関数を以前に紹介したことがありました。
Function Ketugou(MyRange As Range) As String
Dim c As Range
For Each c In MyRange
Ketugou = Ketugou & c
Next
End Function
今回は実行するたびに引数の数が変わってしまうような場合、引数を特定できないときに、
どのようにコードを作るかと言うお話をします。
Sampleとしてユーザー定義関数を作成します。
結論から言えばこのような時は引数にParamArrayを指定してやることで解決します。
またParamArrayを使うときの決まりや、コード内で動的配列を使う方法も紹介しています。
(サンプルファイルは、こちらです。 引数が不定でもParamArrayで大丈夫、ParamArrayでユーザーファンクション、サンプル100回)
以下の図のように、セル範囲の文字列を結合したいケースは、わりとよくあります。
セル範囲が連続している場合は、引数にするセル範囲は1つです。

通常単独で実行されるサブプロシージャーの場合は引数を付けませんけれども、
他のプロシージャーから呼び出す形で使うマクロは、Callステートメントで呼び出すことができます。
そういったプロシージャーには引数を指定することができます。
ファンクションプロシージャーというのもあります。
それはもう関数と同じことですね。関数はサブプロシージャーからも呼ぶことできますし
シート上でユーザ定義関数として使うこともできます。

結合したいセル範囲がいくつもあって、引数が複数になるケースでは、どのようにコードを書けばいいのかというと、 Paramarrayというのを使います。Paramarrayを使うと、プロシージャーの呼び出しの時にその位置の後ろから指定される引数を配列として受け取ります。

そのParamarrayと付けた引数に関しては
その都度、数が変わっても大丈夫なんです。
必ずこのParamarrayというのは、引数複数ある場合は一番最後に指定するという約束があります。
データ型もバリアントということで、これも決まりです。

引数は図のように3つ取ることに決めました。
空白がある場合、空白の扱いをどうするかということで、それを無視するかどうかが、
ignore empty、空白無視をtrueにすれば無視するし、falseにすれば無視しない。
JoinTextは、複数ある可能性のあるセル範囲ですからParamarrayをつけて最後に記述します。

Function ketugou(delimiter As String, ignore_empty As Boolean, _ ParamArray joinText() As Variant) End Function
Functionの名前と引数の部分ができました。
ではコードの部分を書いていきましょう。
結合対象は、
Paramarray で joinTextという配列に 受け取ったものですね。
それはrangeだったりcellだったり rangeだったりcellだったりっていうように、数が不特定です。

3行目を結合していくケースを考えることにしましょう。
離れているかたまりは、セルが複数であろうとも、1つであろうともGroupとします。
またgroupedの中の1つ1つはpieceとよびましょう。

さらにカウント変数のiと動的配列として、Dim newArray() As Variantを宣言します。

for eachで受け取った範囲のgroupedを回します。
For Each piece In grouped
Next piece

でもgroupedはjoinText で複数受け取っていますから、2重For文にしてやる必要があります。
For Each grouped In joinText
For Each piece In grouped
Next piece
Next grouped

外側のFor文では、受け取ったjoinTextの一つのgrouped が範囲なのか単独セルなのかを見ます。
もし範囲ならば、ひとつひとつを見ていくので、
If TypeName(grouped) = "Range" Then
となります。
ここで、空白を無視する場合とそうでない場合に分かれます。

For Each grouped In joinText
If TypeName(grouped) = "Range" Then
For Each piece In grouped
If Not ignore_empty Or Not IsEmpty(piece) Then
ReDim Preserve newArray(i)
newArray(i) = piece
i = i + 1
End If
Next piece
Else
If Not ignore_empty Or Not IsEmpty(grouped) Then
ReDim Preserve newArray(i)
newArray(i) = grouped
i = i + 1
End If
End If
Next grouped
If Not ignore_empty Or Not IsEmpty(piece) Then
空白を無視するか、またはセルが空白で無かったら、その時は
条件にあえば、
ReDim Preserve newArray(i)
というように、ReDimで配列の要素数を変更します。

次にElseのケースでは、groupedが空白じゃなかったら、要素を増やして、
newArray(i) = grouped
と入れるわけです。
コードの全体は次のようになります。
Function ketugou(delimiter As String, ignore_empty As Boolean, _
ParamArray joinText() As Variant)
Dim grouped As Variant, piece As Variant, i As Long
Dim newArray() As Variant
i = 0
For Each grouped In joinText
If TypeName(grouped) = "Range" Then
For Each piece In grouped
If Not ignore_empty Or Not IsEmpty(piece) Then
ReDim Preserve newArray(i)
newArray(i) = piece
i = i + 1
End If
Next piece
Else
If Not ignore_empty Or Not IsEmpty(grouped) Then
ReDim Preserve newArray(i)
newArray(i) = grouped
i = i + 1
End If
End If
Next grouped
ketugou = Join(newArray, delimiter)
End Function

