【ExcelVBA・マクロ】配列を並び替える方法|バブルソートでRange.Sortを使わずにソートする方法【コピペOK】

ExcelVBA

セルに書き出さずに配列のままデータを並び替えたい場面は意外と多くあります。例えば、集計結果を配列に格納したまま順位付けしたり、大量データをシートに書き出す前にメモリ上で整えたりするケースです。こうしたとき、Range.Sortはセル範囲が前提のため使えず、配列そのものを並び替えるロジックが必要になります。

この記事では、配列の並び替えの基本アルゴリズムである「バブルソート」をVBAで実装する方法を、昇順・降順の切り替え方や、数値と文字列を比較するときの注意点まで、コピペしてすぐ使えるサンプルコード付きで解説します。

スポンサーリンク
スポンサーリンク

バブルソートとは

バブルソートは、隣り合う2つの要素を比較して、順序が逆であれば入れ替える、という処理を配列の端から端まで繰り返すことで並び替えを行うアルゴリズムです。仕組みがシンプルで理解しやすいため、VBAで配列を並び替える際の入門的な方法としてよく使われます。

大量データ(数万件以上)には向きませんが、数百〜数千件程度のデータであれば実務上は十分な速度で動作します。

数値配列を昇順に並び替える

まずは基本形として、数値の配列を小さい順(昇順)に並び替えるコードです。

Sub 数値配列を昇順に並び替える()
    Dim 対象配列(1 To 6) As Long
    対象配列(1) = 45
    対象配列(2) = 12
    対象配列(3) = 78
    対象配列(4) = 3
    対象配列(5) = 56
    対象配列(6) = 29

    Dim i As Long, j As Long, 一時退避 As Long

    For i = LBound(対象配列) To UBound(対象配列) - 1
        For j = LBound(対象配列) To UBound(対象配列) - 1 - (i - LBound(対象配列))
            If 対象配列(j) > 対象配列(j + 1) Then
                一時退避 = 対象配列(j)
                対象配列(j) = 対象配列(j + 1)
                対象配列(j + 1) = 一時退避
            End If
        Next j
    Next i

    Dim 結果文字列 As String
    For i = LBound(対象配列) To UBound(対象配列)
        結果文字列 = 結果文字列 & 対象配列(i) & " "
    Next i
    MsgBox "並び替え後:" & 結果文字列
End Sub

外側のFor iループは「あと何周比較すればよいか」を制御し、内側のFor jループで隣同士を比較・入れ替えています。1周するごとに、その周で最大の値が末尾側に確定していくため、内側ループの範囲を少しずつ狭めることで無駄な比較を減らしています。

降順に並び替える

降順(大きい順)にしたい場合は、比較演算子を>から<に変えるだけです。

Sub 数値配列を降順に並び替える()
    Dim 対象配列(1 To 6) As Long
    対象配列(1) = 45
    対象配列(2) = 12
    対象配列(3) = 78
    対象配列(4) = 3
    対象配列(5) = 56
    対象配列(6) = 29

    Dim i As Long, j As Long, 一時退避 As Long

    For i = LBound(対象配列) To UBound(対象配列) - 1
        For j = LBound(対象配列) To UBound(対象配列) - 1 - (i - LBound(対象配列))
            If 対象配列(j) < 対象配列(j + 1) Then
                一時退避 = 対象配列(j)
                対象配列(j) = 対象配列(j + 1)
                対象配列(j + 1) = 一時退避
            End If
        Next j
    Next i
End Sub

昇順・降順を引数で切り替えたい場合は、比較条件をBoolean引数で分岐させる形にすると再利用しやすくなります。

Sub 配列を並び替える(ByRef 対象配列() As Long, Optional ByVal 昇順 As Boolean = True)
    Dim i As Long, j As Long, 一時退避 As Long
    Dim 入れ替える As Boolean

    For i = LBound(対象配列) To UBound(対象配列) - 1
        For j = LBound(対象配列) To UBound(対象配列) - 1 - (i - LBound(対象配列))
            If 昇順 Then
                入れ替える = (対象配列(j) > 対象配列(j + 1))
            Else
                入れ替える = (対象配列(j) < 対象配列(j + 1))
            End If

            If 入れ替える Then
                一時退避 = 対象配列(j)
                対象配列(j) = 対象配列(j + 1)
                対象配列(j + 1) = 一時退避
            End If
        Next j
    Next i
End Sub

配列はByRef(既定の渡し方)で渡すことで、呼び出し元の配列がそのまま並び替えられます。

文字列配列を並び替える場合の注意点

文字列を比較する場合、通常の><演算子は文字コード順で比較されます。日本語のひらがな・カタカナ・漢字が混在すると、五十音順にはならない点に注意してください。

Sub 文字列配列を並び替える()
    Dim 対象配列(1 To 4) As String
    対象配列(1) = "田中"
    対象配列(2) = "佐藤"
    対象配列(3) = "鈴木"
    対象配列(4) = "高橋"

    Dim i As Long, j As Long, 一時退避 As String

    For i = LBound(対象配列) To UBound(対象配列) - 1
        For j = LBound(対象配列) To UBound(対象配列) - 1 - (i - LBound(対象配列))
            If StrComp(対象配列(j), 対象配列(j + 1), vbTextCompare) > 0 Then
                一時退避 = 対象配列(j)
                対象配列(j) = 対象配列(j + 1)
                対象配列(j + 1) = 一時退避
            End If
        Next j
    Next i

    Dim 結果文字列 As String
    For i = LBound(対象配列) To UBound(対象配列)
        結果文字列 = 結果文字列 & 対象配列(i) & " "
    Next i
    MsgBox 結果文字列
End Sub

StrComp関数を使うことで、大文字小文字を区別しない比較(vbTextCompare)や、内部コード順の厳密な比較(vbBinaryCompare)を明示的に指定できます。厳密な五十音順が必要な場合は、読み仮名(フリガナ)を別途取得して、それを比較キーにする方法が確実です。

セルのデータを配列に読み込んでから並び替える

実務では、セル範囲のデータを配列に取り込んでから並び替え、結果だけをシートに書き戻す使い方が多くなります。

Sub セルの値を配列で並び替えてから書き戻す()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets("成績一覧")

    Dim 最終行 As Long
    最終行 = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    Dim 件数 As Long
    件数 = 最終行 - 1  ' 1行目は見出し行のため除く

    Dim 点数配列() As Long
    ReDim 点数配列(1 To 件数)

    Dim i As Long
    For i = 1 To 件数
        点数配列(i) = ws.Cells(i + 1, "B").Value
    Next i

    Dim j As Long, 一時退避 As Long
    For i = LBound(点数配列) To UBound(点数配列) - 1
        For j = LBound(点数配列) To UBound(点数配列) - 1 - (i - LBound(点数配列))
            If 点数配列(j) < 点数配列(j + 1) Then  ' 降順(高得点順)
                一時退避 = 点数配列(j)
                点数配列(j) = 点数配列(j + 1)
                点数配列(j + 1) = 一時退避
            End If
        Next j
    Next i

    For i = 1 To 件数
        ws.Cells(i + 1, "D").Value = 点数配列(i)
    Next i
End Sub

セルへの読み書きは処理速度に影響しやすい部分なので、1件ずつループで読み書きするのではなく、Rangeと配列を一括変換する方法と組み合わせると、より大量のデータでも高速に処理できます。

まとめ

Range.Sortが使えない配列そのものの並び替えには、バブルソートが手軽で理解しやすい選択肢です。ポイントをまとめると以下の通りです。

  • 二重ループで隣同士を比較・入れ替えることで並び替えを行う
  • 比較演算子を><で切り替えるだけで昇順・降順を変更できる
  • 文字列の比較にはStrComp関数を使い、比較方法を明示する
  • セルのデータを配列に取り込んで並び替え、結果だけを書き戻すと効率的

数百〜数千件程度のデータであれば実務上十分な速度で動作するので、配列操作の基本としてぜひマスターしておきましょう。

スポンサーリンク
スポンサーリンク
ExcelVBA
シェアする
いがぴをフォローする

コメント

タイトルとURLをコピーしました