和風スパゲティのレシピ

日本語でコーディングするExcelVBA

複数のセル値を区切り付き文字列に結合する

複数のセルの値を文字列に結合する方法を解説します。

複数のセル値を区切り付き文字列に結合

このような処理をしたいときに活用してください。

対象のセルがn×1のセル範囲の場合

冒頭の画像のように、行方向(縦長)データに処理を行う場合です。

複数のセル値を区切り付き文字列に結合


この場合は比較的短いコードで実装できます。


まずお使いのExcelがExcel2019以降であれば、
TEXTJOIN関数(区切り文字不要ならCONCAT関数)を、
WorksheetFunctionから呼び出して使用できます。

' ↓「みかん,りんご,いちご」が表示されます。
Msgbox WorksheetFunction.TextJoin(",", False, Range("A1:A3"))

' ↓「みかんりんごいちご」が表示されます。
MsgBox WorksheetFunction.Concat(Range("A1:A3"))

 
ただし、この手法は「マクロを使う全ユーザーのExcelが新バージョン」である必要があるため、配布の可能性がある場合は慎重に採用を検討してください。


旧Excelと互換性を保って実装できる方法としては、
VBAのJoin関数ExcelのTRANSPOSE関数を組み合わせて使用します。

' ↓「みかん,りんご,いちご」が表示されます。
MsgBox Join(WorksheetFunction.Transpose(Range("A1:A3"))

 
Join関数は「渡された配列を区切り文字付きで結合する」という、
まさに今回の処理のために用意された関数です。


ただひとつ欠点として、Join関数に渡せる配列は1次元配列限定です。

Rangeオブジェクトは1×nでも2次元配列になってしまうため、
↓このコードでは「型が一致しません」エラーになってしまいます。

MsgBox Join(Range("A1:A3"))

 
そこでセルを1次元配列に変換するために、
WorksheetFunctionのTranspose関数を利用しました。


TRANSPOSE関数とは「配列の行と列を入れ替える」関数で、
本来は↓このような変換を行うための関数です。

本来のTranspose関数の動き


ですがTRANSPOSE関数にはひとつ面白い特徴があり、
できた配列が1×nだった場合は1次元配列に変換」してくれます。

このため、今回のように「2次元配列を1次元配列に変換」するためにも使えます。

対象のセルが1×nのセル範囲の場合

次はセル範囲が画像のように列方向(横長)データの場合のコードです。

列方向データのセル範囲を文字列に結合


こちらも行方向とほとんど変わらないコードで実装できます。

' TEXTJOIN関数を使用
Msgbox WorksheetFunction.TextJoin(",",False,Range("A1:C1"))

' CONCAT関数を使用
MsgBox WorksheetFunction.Concat(Range("A1:C1"))

' Join+Transpose関数を使用
MsgBox Join(WorksheetFunction.Transpose _
                  (WorksheetFunction.Transpose(Range("A1:C1")))

 
唯一変わっているのがJoin+Transpose関数を使う版で、
Transpose関数を2回かませています。


先ほどの説明の通り、Transpose関数は

  • できた配列が1×nだった場合は1次元配列に変換
  • できた配列がn×1だった場合は2次元配列のまま

という仕様になります。


かといってただRange("A1:C1")では2次元配列になってしまいますので、
少し不思議な式ですが、Transpose関数を2度使うことになっています。

対象のセルが飛び飛びの複数セルの場合

最後に対象のセルが飛び飛びの複数エリアからなるセルの場合です。

飛び飛びの複数セルの値を文字列に結合

このようなセルを文字列結合する場合ですね。


これだとレアケースに感じるかもしれませんが、
以下の「オートフィルターで抽出中のセル」などもこれに該当するため、
そんなに稀な状況のコードではありません。

フィルター抽出セルを文字列結合


この場合は、残念ながら1行で行う方法がありません。


VBAのJoin関数が動かないのは当然として、
シート関数のTEXTJOIN(CONCAT)も動きません。

シート上では「複数のセル」を選択できる関数なのですが、
↓このような理屈でVBAで使うことができないのです。

' シート上ではこう書けるため、一見複数セルでも動かせる気がするが・・・
=TEXTJOIN(",",False,A1,B2,C3)


' 上記の式はVBAではこのコードのことであり、これはちゃんと動く
MsgBox TEXTJOIN(",", False, Range("A1"), Range("B2"), Range("C3"))

' VBAでRangeオブジェクトを使う場合はこのコードであり、これは動かない
MsgBox TEXTJOIN(",", False, Range("A1,B2,C3"))

 

よってこの複数エリアにまたがるセルを結合する場合は、
愚直に1セルずつループし、&を用いて結合していく方法を取ることになります。

そのコードがこちらです。

Sub 複数セルの文字列を結合する()
    
    ' 例:フィルター抽出集のセルだけを結合
    Dim 対象セルリスト As Range
    Set 対象セルリスト = Range("A2:A6").SpecialCells(xlCellTypeVisible)
    
    ' すべてのセル値をカンマで結合
    Dim 結合文字列 As String
    Dim cell As Range
    For Each cell In 対象セルリスト
        結合文字列 = 結合文字列 & "," & cell.Value
    Next

    ' 最初のカンマを消去
    結合文字列 = Mid(結合文字列, 2)
    
End Sub

お手本のような「すべてのセルをForEachで処理」するコードですね。

すべてのセル値を "," と一緒につないでいくコードです。


For Eachの終了段階では、結合文字列は「,」から始まっていますので、
Mid関数で「2文字目以降」を取り出すことで最初の「,」を消しています。


↓のように書いても動きますが、コードが複雑化してしまいがちになるので、
こういった例外処理はなるべくFor文の外に出してあげましょう。

Dim 結合文字列 As String
Dim cell As Range
For Each cell In 対象セルリスト
    If 結合文字列 = "" Then ' ←初回のためだけのIf文がFor文を3行⇒7行に増やしてしまう
        結合文字列 = cell.Value
    Else
        結合文字列 = 結合文字列 & "," & cell.Value
    End If
Next

 

汎用関数化

上記のコードは単純ではありますが、
この処理のためにいちいち書くのは面倒です。

この処理をよく行う方は、汎用関数として切り出しておき、
今後は1行で実行できるようにしておきましょう。


そのコードがこちらです。

' 汎用関数
Function Join_Range版(対象セルリスト As Range, 区切り文字 As String) As String
    Dim cell As Range
    For Each cell In 対象セルリスト.Cells
        Join_Range版 = Join_Range版 & 区切り文字 & cell.Value
    Next
    
    Join_Range版 = Mid(Join_Range版, 2)
End Function

' 実行例
MsgBox Join_Range版(Range("A2:A6").SpecialCells(xlCellTypeVisible), ",")

これで本家Joinと同じように、1行で実行できるようになりました。

しかもこの関数は、1×n、n×1のセル範囲にも対応しているため、
1度作ってしまえばすべてのセル範囲に対してこの関数だけで対応ができます。

もうTextjoinもTransposeの使用も不要ですね。


こういった「単純だけど書くのが面倒な処理」というのは、
えてして汎用関数が作りやすく、そして使いやすいです。


よく使うコードは汎用関数にして持っておきましょう。



ついでに関数の使い勝手を良くするための小ネタですが、
まず関数名を本家Joinと同じ書き出しにすることで、

Joinからの選択肢

この選択肢をCtrl+Spaseで出せるようになります。

元の関数をカスタマイズした関数を作った場合は、
元の関数名から始まる関数名にしておくと便利ですね。



また、Optionalキーワードを使って「引数を省略可能」にし、

Function Join_Range版(対象セルリスト As Range _
    , Optional 区切り文字 As String = ",") As String

このような引数にすることで、省略時はカンマで区切ってくれるようになります。


よく使う区切り文字がある方は、その文字を採用してみてください。


汎用関数についての解説はこちらをどうぞ。
www.limecode.jp