Copilotで列番号を動的管理|階層連動VBAの実務改善

前回コードの問題点

前回は

  • 階層連動
  • 条件付きユニーク化
  • 結合条件での抽出

ここまで完成しました。

正直、この時点で「動く仕組み」はできています。

ただし、実務ではここで必ず問題が出ます。

それが 列に依存しすぎている です。

例えば今回のコードは

  • D列=調査票名
  • E列=項目名
  • F列=品目名
  • G列=アイテム名

と「固定」されています。

一見問題なさそうですが、実務ではこうなります。

  • 列の順番が変わる
  • 新しい列が追加される
  • レイアウトが変更される

この瞬間に、全部壊れます。これが現場で一番多いトラブルです。

なので今回は 列番号を動的に管理 していきます。

Copilotで列番号を動的管理|階層連動VBAの実務改善

今回取り扱うデータも経済産業省の公開データ「生産動態統計年報(2024年)」です。

今回やること

やることはシンプルです。

  • Dictionaryで項目名 → 列番号を管理
  • 列番号のハードコーディングを廃止
  • 可読性を維持したまま柔軟化

この一手で「壊れにくいコード」になります。

実際のCopilotとのやり取り

利用者 → Copilot

既存の階層連動コンボボックスのコードを改善してください。

■修正内容
・列番号(D,E,F,G)を固定せず、項目名から動的に取得する
・1行目のヘッダーから列番号を特定する
・Dictionaryで「項目名 → 列番号」を管理
・既存のロジック(階層連動・ユニーク化)は維持 ■対象項目
・調査票名
・項目名
・品目名
・アイテム名 ■その他
・可読性重視でコメント付き
・コードは省略せず全文提示

Copilot → 利用者

以下に、列番号を動的に管理するよう改善した完全版コードを提示します。

Option Explicit
'---------------------------------------------
' 列名 → 列番号 を保持する Dictionary
'---------------------------------------------
Private ColIndex As Object   'Dictionary

'---------------------------------------------
' フォーム初期化:列番号取得 → 調査票名のみロード
'---------------------------------------------
Private Sub UserForm_Initialize()

    '列番号をヘッダーから取得
    Set ColIndex = CreateObject("Scripting.Dictionary")
    Call InitColumnIndex

    '最上位の調査票名をロード
    Load調査票名
End Sub

'---------------------------------------------
' ヘッダー行(1行目)から列番号を取得
'---------------------------------------------
Private Sub InitColumnIndex()
    Dim ws As Worksheet
    Dim lastCol As Long
    Dim i As Long
    Dim header As String

    Set ws = ThisWorkbook.Worksheets("data")
    lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column

    '1行目のヘッダーを走査して Dictionary に登録
    For i = 1 To lastCol
        header = Trim(ws.Cells(1, i).Value)
        If header <> "" Then
            If Not ColIndex.Exists(header) Then
                ColIndex.Add header, i
            End If
        End If
    Next i
End Sub

'---------------------------------------------
' 調査票名変更 → 項目名を再ロード
'---------------------------------------------
Private Sub cmb調査票名_Change()
    cmb項目名.Clear
    cmb品目名.Clear
    cmbアイテム名.Clear

    If cmb調査票名.ListIndex >= 0 Then
        Load項目名 cmb調査票名.Value
    End If
End Sub

'---------------------------------------------
' 項目名変更 → 品目名を再ロード
'---------------------------------------------
Private Sub cmb項目名_Change()
    cmb品目名.Clear
    cmbアイテム名.Clear

    If cmb調査票名.ListIndex >= 0 And cmb項目名.ListIndex >= 0 Then
        Load品目名 cmb調査票名.Value, cmb項目名.Value
    End If
End Sub

'---------------------------------------------
' 品目名変更 → アイテム名を再ロード
'---------------------------------------------
Private Sub cmb品目名_Change()
    cmbアイテム名.Clear

    If cmb調査票名.ListIndex >= 0 _
       And cmb項目名.ListIndex >= 0 _
       And cmb品目名.ListIndex >= 0 Then

        Loadアイテム名 cmb調査票名.Value, cmb項目名.Value, cmb品目名.Value
    End If
End Sub

'===========================================================
'  調査票名のロード(最上位)
'===========================================================
Private Sub Load調査票名()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim dic As Object
    Dim i As Long
    Dim col調査票 As Long
    Dim val As String

    Set ws = ThisWorkbook.Worksheets("data")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    Set dic = CreateObject("Scripting.Dictionary")

    '列番号を Dictionary から取得
    col調査票 = ColIndex("調査票名")

    '調査票名をユニーク化
    For i = 2 To lastRow
        val = Trim(ws.Cells(i, col調査票).Value)
        If val <> "" Then
            If Not dic.Exists(val) Then dic.Add val, True
        End If
    Next i

    cmb調査票名.List = dic.Keys
End Sub

'===========================================================
'  項目名のロード(調査票名 → 項目名)
'===========================================================
Private Sub Load項目名(ByVal 調査票 As String)
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim dic As Object
    Dim i As Long
    Dim col調査票 As Long, col項目 As Long
    Dim val As String

    Set ws = ThisWorkbook.Worksheets("data")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    Set dic = CreateObject("Scripting.Dictionary")

    col調査票 = ColIndex("調査票名")
    col項目 = ColIndex("項目名")

    For i = 2 To lastRow
        If Trim(ws.Cells(i, col調査票).Value) = 調査票 Then
            val = Trim(ws.Cells(i, col項目).Value)
            If val <> "" Then
                If Not dic.Exists(val) Then dic.Add val, True
            End If
        End If
    Next i

    cmb項目名.List = dic.Keys
End Sub

'===========================================================
'  品目名のロード(調査票名 × 項目名 → 品目名)
'===========================================================
Private Sub Load品目名(ByVal 調査票 As String, ByVal 項目 As String)
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim dic As Object
    Dim i As Long
    Dim col調査票 As Long, col項目 As Long, col品目 As Long
    Dim val As String

    Set ws = ThisWorkbook.Worksheets("data")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    Set dic = CreateObject("Scripting.Dictionary")

    col調査票 = ColIndex("調査票名")
    col項目 = ColIndex("項目名")
    col品目 = ColIndex("品目名")

    For i = 2 To lastRow
        If Trim(ws.Cells(i, col調査票).Value) = 調査票 _
           And Trim(ws.Cells(i, col項目).Value) = 項目 Then

            val = Trim(ws.Cells(i, col品目).Value)
            If val <> "" Then
                If Not dic.Exists(val) Then dic.Add val, True
            End If
        End If
    Next i

    cmb品目名.List = dic.Keys
End Sub

'===========================================================
'  アイテム名のロード(調査票名 × 項目名 × 品目名 → アイテム名)
'===========================================================
Private Sub Loadアイテム名(ByVal 調査票 As String, _
                           ByVal 項目 As String, _
                           ByVal 品目 As String)

    Dim ws As Worksheet
    Dim lastRow As Long
    Dim dic As Object
    Dim i As Long
    Dim col調査票 As Long, col項目 As Long, col品目 As Long, colアイテム As Long
    Dim val As String

    Set ws = ThisWorkbook.Worksheets("data")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    Set dic = CreateObject("Scripting.Dictionary")

    col調査票 = ColIndex("調査票名")
    col項目 = ColIndex("項目名")
    col品目 = ColIndex("品目名")
    colアイテム = ColIndex("アイテム名")

    For i = 2 To lastRow
        If Trim(ws.Cells(i, col調査票).Value) = 調査票 _
           And Trim(ws.Cells(i, col項目).Value) = 項目 _
           And Trim(ws.Cells(i, col品目).Value) = 品目 Then

            val = Trim(ws.Cells(i, colアイテム).Value)
            If val <> "" Then
                If Not dic.Exists(val) Then dic.Add val, True
            End If
        End If
    Next i

    cmbアイテム名.List = dic.Keys
End Sub

この修正の本質

ここが一番重要です。

今回の本質は、列依存を排除したです。

今までは「ws.Cells(i, “D”)」のように書いていました。

これは「列が固定されている前提」です。

今回からは「colMap(“調査票名”)」

つまり、意味で列を取得しています。

これにより

  • 列が移動しても壊れない
  • 列が追加されても対応できる
  • メンテナンスが圧倒的に楽

になります。

なぜDictionaryを使うのか

ここも重要です。

Dictionaryは

  • キーで高速アクセスできる
  • 構造がシンプル
  • 拡張しやすい

今回のように 項目名 → 列番号 の管理には最適です。

実務では名前で管理する」これが基本です。

実務でのインパクト

この修正、かなり効きます。

  • Excelレイアウト変更に強くなる
  • 修正コストが激減する
  • 他の人が見ても理解しやすい

そして何より長く使えるコードになる」ここが大きいです。

次回やること

次回以降

  • 並び順の統一
  • UI改善(選択してください)
  • 高速化(配列化)

を追加していきます。

ここまでやると、完全に業務アプリレベルになります。

まとめ

今回は 列番号の動的管理 を追加しました。

やっていることはシンプルですが、実務ではかなり重要な改善です。

Excelは、構造が変わる前提で作る必要があります。

今回のように、列を「番号」ではなく「意味」で扱う。

これができるようになると、一気に実務レベルに入ります。

そして今回もポイントは同じです。

自分でコードを書いていない

  • 仕様を整理
  • プロンプトを書く

これだけです。

ただし、ここからは「改善力」が求められます。

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

リンク

ChatGPTでExcel VBAマクロを自動生成してみた|初心者でも使える実践手順
Excel VBAのエラー修正をChatGPTに頼んだら原因が一発で分かった話
仕様が曖昧でもOK?ChatGPTにExcel VBAを作らせてみた結果
ChatGPTに作らせたExcel VBAコードは安全?使う前に確認すべきポイント
Excel VBA初心者がChatGPTを使うと挫折しにくくなる理由
CopilotでExcel作業はどこまで効率化できる?実務で使ってみた結果
Copilotに仕事を任せてみた|メール・資料作成はここまで楽になる
Copilotが向いている人・向いていない人を実体験から整理してみた
ChatGPT・Copilot・Geminiに同じ仕事を頼んだら結果が全然違った
結局どれを使えばいい?目的別に生成AIを整理してみた【初心者向け】
初心者が最初にやるべき生成AI活用① ChatGPTで「長文を理解・整理する」読むのがしんどい人ほど効果が出る使い方
初心者が最初にやるべき生成AI活用②ChatGPTに「分からないことをそのまま相談する」使い方・聞き方を気にしない安心感
初心者が最初にやるべき生成AI活用③AIを「自分専用の調べ物係」にする使い方|検索に疲れた人ほど効果が出る
ChatGPTにブログ記事を“赤ペン先生”してもらったら修正力がすごかった話―書くのが苦手な人ほど使ってほしい文章改善―
CopilotでExcel作業手順書を一瞬で作る|引き継ぎ資料が秒で完成した話
Geminiでアイデアが枯れたときの発想復活法|何も思いつかないを脱出する使い方
ChatGPT・Copilot・Geminiをブレスト役で使い分ける実例|発想が加速した方法
CopilotでExcel集計→Word報告書を一気に作る実務フロー― 月次報告が“考えずに終わる”ようになった話 ―
Copilotで会議議事録からToDo管理まで一気にやってみた実務フロー
Geminiで社内資料の画像を作ってみた|伝わらない資料が一瞬で変わった話
ChatGPTに流行りの「今まで私があなたをどう扱ってきたかを画像にして」を頼んでみた
ChatGPTのプロンプトを“自分用”に進化させる方法|頼み方で結果が激変する理由
初心者が最初にやりがちなプロンプトの失敗例と直し方
ChatGPTが急に賢くなる「前提条件」の渡し方-同じ質問なのに答えが変わるのはなぜ?-
プロンプトを毎回書かなくてよくなる“会話の残し方”
ChatGPTに「察してもらえない」と感じた時の原因と対処法|噛み合わない理由を実例で解説
AIに雑に投げても失敗しにくくなる頼み方のコツ|考えずに使っても噛み合う方法
プロンプトが思いつかない人のための“最初の一文”テンプレ集
ChatGPTを“相棒化”する人と、うまく使えない人の決定的な差
生成AIはどこまで使っていい?初心者が最初に知るべき法律の話
AI画像生成は違法?著作権で“やってはいけないこと”実例集
商用利用OK?NG?生成AIの利用規約を初心者向けに噛み砕く
AIで作った文章は誰のもの?著作権の考え方をやさしく解説
「知らなかった」では済まない?生成AIトラブル事例と回避法
ブログ・SNSでAI生成物を使うときの最低限の注意点まとめ
AIに頼りすぎて思考力が落ちた話|便利さの裏で起きたリアル失敗
AIの回答を信じて怒られた話|正しいはずが通用しなかった理由
ChatGPTにExcelマクロを直してもらったらコメントが消えた話|親切の落とし穴
Copilotの差分コードが分からなかった話|全コード出してもらう選択
ChatGPTにきれいな文章はいらなかった話|ぐちゃぐちゃでも伝わる
ChatGPTは仕事に使うだけじゃない|高校生の恋愛LINEをChatGPTに相談したら既読スルーが減った話
ChatGPTは仕事に使うだけじゃない|バツイチ48歳のマッチングアプリ不安を解消した相談実例
ChatGPTは仕事に使うだけじゃない|社内不倫の相談を法と気持ちで整理した話
ChatGPTは仕事に使うだけじゃない|離婚前に確認すべきことを全部整理してくれた話
ChatGPTは仕事に使うだけじゃない|夫が不倫しているかも…調べ方を相談してみた話
ChatGPTは仕事に使うだけじゃない|高校生の反抗期の息子への声かけを相談してみた話
ChatGPTは仕事に使うだけじゃない|大阪のマンションごみトラブルを理事会で相談した話
ChatGPTは仕事に使うだけじゃない|社内報の“ビックリマン風・ジブリ風”画像を作った話
ChatGPTは仕事に使うだけじゃない|ママ友ランチ角が立たず断れた話
ChatGPTは仕事に使うだけじゃない|会話ゼロ夫婦が再出発できた話
ChatGPTは仕事に使うだけじゃない|派遣切り50代女性の次の仕事を相談した話
ChatGPTは仕事に使うだけじゃない|これから役立つ資格をAIに本気で相談してみた話
ChatGPTは仕事に使うだけじゃない|親の介護が不安で今から準備を聞いた話
ChatGPTは仕事に使うだけじゃない|PTA役員を角が立たず断れた話
ChatGPTは仕事に使うだけじゃない|義母との距離感がしんどい悩みを整理してもらった話
ChatGPTは仕事に使うだけじゃない|家計が苦しい時に大阪市で使える制度を年齢別に聞いた話
ChatGPTは仕事に使うだけじゃない|子どものスマホルールを一緒に作ってみた話
ChatGPTは仕事に使うだけじゃない|推し活にお金を使いすぎる悩みを整理してもらった話
ChatGPTは仕事に使うだけじゃない|人間関係がしんどくて距離を置きたい時の相談実例
ChatGPTは仕事に使うだけじゃない|将来が不安で眠れない50歳手前女性の相談を整理してもらった話
Copilotでクロス表を縦持ちデータに変換|実務VBA作成の流れ
Copilotでクロス表を縦持ちデータに変換|実務仕上げ編
Copilotでクロス表を縦持ちデータに変換|配列で高速化する
Copilotでクロス表を縦持ちデータに変換|項目変更に強い構造へ
Copilotでリストボックスにユニーク値を設定|実務VBA
Copilotでリストボックス並び替え対応|実務VBA
Copilotでリストボックス表示制御を追加|実務VBA
Copilotで階層連動コンボボックス作成|実務VBA
Copilotで列番号を動的管理|階層連動VBAの実務改善

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