[[20240504221031]] 『空白セルを削除して左に詰める・・・をコードに追』(りゆうの) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『空白セルを削除して左に詰める・・・をコードに追加』(りゆうの)

https://www.excel.studio-kazu.jp/kw/20240503213030.html
の質問に便乗させていただきます。

当方はこのような一覧表を使っています。

A列   B列   C列   D列   E列   F列
日付  曜日  外出者  外出者  外出者  外出者
1    水   伊藤   鈴木   田中
2    木   鈴木   坂本        佐々木
3    金
6    月   森 
7    火   長谷川       田中    加藤

この一覧表を以下のようにする場合、

A列   B列   C列
日付  曜日  外出者
1    水   伊藤
1    水   鈴木
1    水   田中
2    木   鈴木
2    木   坂本
2    木   佐々木
3    金
6    月   森 
7    火   長谷川
7    火   田中
7    火   加藤

Sub test()

        Dim v, w, i&, j&, k&
        v = Cells(1).CurrentRegion
        ReDim w(1 To (UBound(v, 2) - 2) * UBound(v), 1 To 3)
        w(1, 1) = "日付": w(1, 2) = "曜日": w(1, 3) = "外出者"
        k = 2
        For i = 2 To UBound(v)
            For j = 3 To UBound(v, 2)
                w(k, 1) = v(i, 1)
                w(k, 2) = v(i, 2)
                If v(i, j) = Empty Then
                    If j = 3 Then k = k + 1
                    Exit For
                Else
                    w(k, 3) = v(i, j)
                    k = k + 1
                End If
            Next
        Next
        With Worksheets.Add.Cells(1).Resize(UBound(w), 3)
            .Value = w
            .Columns(1).NumberFormatLocal = "d"
            .Columns(2).NumberFormatLocal = "aaa"
        End With
    End Sub

のコードのどこに何を足せば上のような表にできるのか
どなたか教えていただけないでしょうか?

[20240503213030]の表との違いは
名前と名前の間に空白セルがあることです。
最初の一覧表の時点で《Ctrl+G》を使って空白セルを左詰めにする方法もありますが、
当方はVBAのコードに組み込みたいのが希望です。
よろしくお願いいたします。

< 使用 Excel:Excel2019、使用 OS:Windows10 >


一般操作でできるのならマクロの記録をしてみるとVBAコードがわかる
(ぞ) 2024/05/05(日) 04:59:34

 前の質問者さんじゃないんですか?
 それは置くとして、VBAで空白セルを左詰めにする方法でいいと思いますよ?
 どこまでご自分でトライされているんですか?
 それを書いたうえで質問されたほうがよいと、個人的には思いました。

 配列を使わない方法で書いて見ました。参考にしてみてください。
 Sub test()
     Dim ws1 As Worksheet, ws2 As Worksheet
     Dim rng As Range
     Dim p&, num&, k&

     Set ws1 = Worksheets("Sheet1")  '■要修正
     Set ws2 = Worksheets.Add

     With ws1
         '空白セルを左詰め
         Set rng = .[A1].CurrentRegion
         rng.SpecialCells(xlCellTypeBlanks).Delete Shift:=xlToLeft

         '横持データを縦持ちデータに変換
         ws2.[A1:C1].Value = .[A1:C1].Value   '見出し行を作成
         p = 2
         For k = 2 To .Cells(Rows.Count, "A").End(xlUp).Row
             num = .Cells(k, Columns.Count).End(xlToLeft).Column - 2    'その日の外出者の数
             If num = 0 Then         '外出者が無い場合は日付だけコピーペイスト
                 .Cells(k, 1).Resize(1, 2).Copy ws2.Cells(p, "A")
                 p = p + 1
             Else                    'それ以外は、外出者を縦に並べる
                 .Cells(k, 1).Resize(1, 2).Copy ws2.Cells(p, "A").Resize(num, 2)
                 ws2.Cells(p, "C").Resize(num, 1) = _
                         Application.Transpose(.Cells(k, "C").Resize(1, num))
                 p = p + num
             End If
         Next
     End With
 End Sub

 ちなみに、前回スレッドのコードですが、
 最後の日が外出者ひとりとかだと、
 日付だけの行が一行余分に書き出されませんでしたか? 少し気になりました。

(xyz) 2024/05/05(日) 06:32:09


 >日付だけの行が一行余分に書き出されませんでしたか? 少し気になりました。
 xyzさん、ご指摘ありがとうございます。
 まさにその通りでありまして、お恥ずかしい限りです。
 質問者さんにはご迷惑お掛け致しました。(前スレ訂正しました)

     Sub test()
        Dim v, w, x
        Dim i&, j&, k&
        Dim flg As Boolean
        With Cells(1).CurrentRegion
            x = .Value
            On Error Resume Next
            .SpecialCells(xlCellTypeBlanks).Delete xlToLeft
            On Error GoTo 0
            v = .Value
            .Value = x
        End With
        ReDim w(1 To (UBound(v, 2) - 2) * UBound(v), 1 To 3)
        w(1, 1) = "日付": w(1, 2) = "曜日": w(1, 3) = "外出者"
        k = 2
        For i = 2 To UBound(v)
            flg = False
            For j = 3 To UBound(v, 2)
                If Not v(i, j) = Empty Then
                    w(k, 1) = v(i, 1)
                    w(k, 2) = v(i, 2)
                    w(k, 3) = v(i, j)
                    k = k + 1
                    flg = True
                End If
            Next
            If Not flg Then
                w(k, 1) = v(i, 1)
                w(k, 2) = v(i, 2)
                k = k + 1
            End If
        Next
        With Worksheets.Add.Cells(1).Resize(k, 3)
            .Value = w
            .Columns(1).NumberFormatLocal = "d"
            .Columns(2).NumberFormatLocal = "aaa"
        End With
    End Sub
(羊カウント) 2024/05/05(日) 08:14:31

既に解決しているとおもいますが、研究用として私も2案ほど提示しておきます。
 ※完成品プレゼントの意図はありませんので、そのまま丸パクリはご遠慮ください。

■1

 《Ctrl+G》を使って空白セルを左詰めはそのまま採用
 (ただし、元データをいじらないように作業用シートに丸ごとコピーしてから作業するように変更)

 A〜C列までは強制的にコピー
 D列以降は、データがある部分までA〜B及びその列のデータをコピー

    Sub 配列なしバージョン()
        Dim tmpSH As Worksheet, dstSH As Worksheet
        Dim 行 As Long, 出力行 As Long, 列 As Long

        Worksheets("Sheet1").Copy after:=Worksheets("Sheet1")

        Set tmpSH = Worksheets("Sheet1").Next
        Set dstSH = Worksheets.Add

        tmpSH.Range("A1").CurrentRegion.SpecialCells(xlCellTypeBlanks).Delete Shift:=xlToLeft
        tmpSH.Rows(1).Delete
        Worksheets("Sheet1").Range("A1:C1").Copy dstSH.Range("A1")

        出力行 = 2

        With tmpSH.Range("A1").CurrentRegion
            For 行 = 1 To .Rows.Count
                .Cells(行, 1).Resize(, 3).Copy dstSH.Cells(出力行, "A")
                出力行 = 出力行 + 1

                For 列 = 4 To tmpSH.Cells(行, Columns.Count).End(xlToLeft).Column
                    Union(.Cells(行, 1).Resize(, 2), .Cells(行, 列)).Copy dstSH.Cells(出力行, "A")
                    出力行 = 出力行 + 1
                Next 列
            Next 行
        End With

        Application.DisplayAlerts = False
        tmpSH.Delete
        Application.DisplayAlerts = True
    End Sub

D列以降に複数データがある場合、xyzさんは行列入れ替えで一括処理をしていますが、上記は組み合わせごとに逐一コピペしているのでデータ数が多い場合はその分時間がかかることになるので速度的に不利だとおもいます。

 Transposeで入れ替えられないほどデータがあるときなどは一考の価値があるかもしれません。

■2

 上記の発想の延長。

    Sub 配列使用バージョン()
        Dim 行 As Long, 列 As Long, x As Long
        Dim 配列() As Variant

        With Worksheets("Sheet1").Range("A1").CurrentRegion
            .Cells(1, 1).Resize(, 3).Copy dstSH.Range("A1")

            ReDim 配列((.Rows.Count - 1) * (Columns.Count - 1), 2)

            For 行 = 2 To .Rows.Count
                配列(x, 0) = .Cells(行, "A").Value
                配列(x, 1) = .Cells(行, "B").Value
                配列(x, 2) = .Cells(行, 3).Value
                x = x + 1
                For 列 = 4 To .Columns.Count
                    If .Cells(行, 列).Value <> "" Then
                        配列(x, 0) = .Cells(行, "A").Value
                        配列(x, 1) = .Cells(行, "B").Value
                        配列(x, 2) = .Cells(行, 列).Value
                        x = x + 1
                    End If
                Next 列
            Next 行
        End With

        With Worksheets.Add
            .Range("A1:C1").Value = Array("日付", "曜日", "外出者")
            .Range("A2").Resize(UBound(配列, 1) + 1, UBound(配列, 2) + 1).Value = 配列
        End With

    End Sub

羊カウントさんのコードとほぼ変わりませんが、空白セルを左詰めする作業はせず、「■1」と同様、C列は強制取得、D列以降はデータがあるとき(""でないとき)だけ取得するようにしています。

■3
余談になりますが、配列を使うほうは[[20240415115253]]でも話題にあがりましたので、そちらも参考にされるとよいとおもいます。

(もこな2) 2024/05/05(日) 12:49:06


 羊カウントさん 
 早速の対応に、質問者さんに成り代わって感謝申し上げます。お疲れさまでした。
(xyz) 2024/05/05(日) 16:37:58

XYZ 様
前の質問者さんと酷似の表を使っているので
その表をお借りして質問しました。
VBAは勉強を始めたばかりでほとんど何もわからず
トライしたのはExcelの基本操作でなんとかできないか、くらいでした(それが《Ctrl+G》でした)

お書きいただいたコードで期待通りの一覧表に変更することができました。
大変助かりました。
ありがとうございました。

(りゆうの) 2024/05/05(日) 20:30:19


羊カウント 様
日付だけの行が一行余分に書き出されてしまう件と、
空白セルを左詰めする件と合わせて、
前回のコードと訂正されたコードを見比べ
どこをどう修正したらいいのかを勉強するための参考にいたします。
(とはいえ、当方、まだまだそこまでのレベルまで達していないのですが)
ご丁寧な回答ありがとうございました。

(りゆうの) 2024/05/05(日) 20:32:04


もこな2 様
2案、そして過去スレの紹介、ありがとうございます。
VBAは勉強を始めたばかりで現段階ではわからないことだらけなのですが、
2案と過去スレの内容は、自力で完成度の高いコードが書けるようになるための参考になります。
ご丁寧な回答ありがとうございました。

(りゆうの) 2024/05/05(日) 20:34:16


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.