質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

Q&A

解決済

4回答

867閲覧

VBAでテーブル形式のリストからクロス形式のリストを作成したい(その際、特定の文字を含まない場合、次列のセルに移動する指示を出したい)

shibakoppe

総合スコア35

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

0グッド

0クリップ

投稿2022/05/12 01:29

編集2022/05/12 08:13

お世話になります。
VBA及びマクロ初心者ですが、皆様のお力を貸していただきたく思います。

現在、Excelで以下のようなテーブル形式の表から別シートへクロス形式の一覧表を作成する為のマクロに取り組んでおります。

<リスト1のシート>

番号属性
1000A
1000B
1000C
1001A
1001B
1001C
1002E
1000D
1000E

------以下略------

<リスト2のシート>

番号属性1属性2属性3属性4属性5
1000ABCDE
1001ABC
1002E

------以下略------

リスト2のシートへの転記のコードが以下となります。
(ネット上に挙げられていたものを引用・編集させていただきました。)

Private Sub CommandButton1_Click() Call A Call B Call C Call D Call E End Sub --------------------------------------------------------------------------------- Sub A() Dim i As Long Dim a() As Variant Dim j As Long j = 1 For i = 1 To Sheets("リスト1").Range("B" & Rows.Count).End(xlUp).Row 'セルにAがあるか If Sheets("リスト1").Cells(i, 2).Value Like "*A*" Then ReDim Preserve a(j) a(j) = Sheets("リスト1").Cells(i, 2).Value j = j + 1 End If Next For j = LBound(a) To UBound(a) Sheets("リスト2").Cells(j + 2, 2).Value = a(j) Next End Sub --------------------------------------------------------------------------------- Sub B() Dim i As Long Dim a() As Variant Dim j As Long j = 1 For i = 1 To Sheets("リスト1").Range("B" & Rows.Count).End(xlUp).Row 'セルにBがあるか If Sheets("リスト1").Cells(i, 2).Value Like "*B*" Then ReDim Preserve a(j) a(j) = Sheets("リスト1").Cells(i, 2).Value j = j + 1 End If Next For j = LBound(a) To UBound(a) Sheets("リスト2").Cells(j + 2, 3).Value = a(j) Next End Sub --------------------------------------------------------------------------------- Sub C() Dim i As Long Dim a() As Variant Dim j As Long j = 1 For i = 1 To Sheets("リスト1").Range("B" & Rows.Count).End(xlUp).Row 'セルにCがあるか If Sheets("リスト1").Cells(i, 2).Value Like "*C*" Then ReDim Preserve a(j) a(j) = Sheets("リスト1").Cells(i, 2).Value j = j + 1 End If Next For j = LBound(a) To UBound(a) Sheets("リスト2").Cells(j + 2, 4).Value = a(j) Next End Sub --------------------------------------------------------------------------------- Sub D() Dim i As Long Dim a() As Variant Dim j As Long j = 1 For i = 1 To Sheets("リスト1").Range("B" & Rows.Count).End(xlUp).Row 'セルにDがあるか If Sheets("リスト1").Cells(i, 2).Value Like "*D*" Then ReDim Preserve a(j) a(j) = Sheets("リスト1").Cells(i, 2).Value j = j + 1 End If Next For j = LBound(a) To UBound(a) Sheets("リスト2").Cells(j + 2, 5).Value = a(j) Next End Sub --------------------------------------------------------------------------------- Sub E() Dim i As Long Dim a() As Variant Dim j As Long j = 1 For i = 1 To Sheets("リスト1").Range("B" & Rows.Count).End(xlUp).Row 'セルにEがあるか If Sheets("リスト1").Cells(i, 2).Value Like "*E*" Then ReDim Preserve a(j) a(j) = Sheets("リスト1").Cells(i, 2).Value j = j + 1 End If Next For j = LBound(a) To UBound(a) Sheets("リスト2").Cells(j + 2, 6).Value = a(j) Next End Sub ---------------------------------------------------------------------------------

このコードに加えて、「リスト1の番号とリスト2の番号が一致する場合」という条件式と、「リスト1の属性に該当の属性が入力されていない場合、空白処理をする」という指示をしたいのです。

リスト1作成時の条件・注意点として
・番号は4桁で基本的には連番となるが、若い番号が行の最後に入る可能性がある
・属性列はA~Dまで必ずしもすべてが順番通りに入るとは限らない
・リスト1には連続で入力されるため空白セルは存在しない
以上のことが挙げられます。
最終的なリスト2の形といたしまして「リスト1の番号とリスト2の番号が一致したとき」、その番号に対して1行の表を作成したく、「該当の属性が存在しない場合」は空白処理をして、次の番号に対する表を作成する指示を出したいのです。
また、後から若い番号の属性データが入力されていた場合、若い番号の行に戻って該当する属性列に入力されるようにしたいのですが、いくら調べても対応できるコード見つけることができませんでした。

作成したい表は明確なのですが、うまく文章にすることができず、伝わりにくい箇所も多々あると思いますが、検索の仕方や利用できそうなコードがありましたらご教示いただけますと幸いです。

加えて上記の各コードの

For j = LBound(a) To UBound(a) Sheets("リスト2").Cells(j + 2, ○).Value = a(j) Next

この部分で必ずデバックが上がってしまうのですが、それについての対策や考えられるエラーの理由等もご教示いただけますと嬉しく思います。

私自身の力・経験・知識不足で長々と書き連ねてしまい、大変申し訳ございませんが、皆様のお力をお貸しいただけますと幸いです。
何卒、宜しくお願い申し上げます。

※追記1※
コードの使用につきまして、こちらにも記述させていただきます。
目的といたしましてはリスト2の作成ですので、上記のコードを使用せず作業することもできるかと思います。
ただ、誰が作業してもシンプルでわかりやすくしたかったため、ボタンを用いる方が直感的でわかりやすいのではないかと思いこの方法を取らせていただいております。
(最終的にリスト2のデータを使用する為、数式等は入らず文字列として転記する必要があったことから等号で反映させることもしたくなかったのです。)

また、属性につきましては文言は伏せさせていただいておりますが、A~Dは固定となっております。リスト2の番号は入力済みであることを前提に作業を進める予定です。

さらにAccessを使用したほうがわかりやすいのだとは思うのですが、作業環境にAccessがないため候補から除外させていただいております。

改めて、宜しくお願い致します。

※追記2※
ご回答いただき誠にありがとうございます。
コメント欄で追加で質問させていただいておりましたが、失礼だったと思います。
大変申し訳ございません。
改めて、こちらに記載させていただきます。

現在、h-okhs様、hatena19様お二人の方法を試させていただき、どちらもしっかり表を作成することに成功いたしました。
本当にありがとうございました。
以下、追加での質問となります。
属性が「1_A_○」や「2_B_○」といった文字列になっている際、「A」や「B」をキー項目として、そのセル丸ごとを転記する際は、質問にも載せさせていただいているIFから始まるコードを使用しても問題ないでしょうか?

申し訳ございませんが、この点につきましてもご教授いただけますと幸いです。
よろしくお願いいたします。

※追記3※
ご回答いただいた皆様、誠にありがとうございました。
拙い文章から私の目的をご理解いただき、なんとお礼を申し上げたらよいか…。
どの回答をBAにするかすごく迷いましたが、シンプルで分かりやすかったこと、丁寧に導いてくださったhatena19様とさせていただきました。
改めて、皆様お世話になりました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

h-okhs

2022/05/12 02:13 編集

このネットで拾ってきたとおっしゃっているイケてないコードを修正して完成させる必要があるのか、それとも目的はリスト2の表を作ることで、このコードは使わなくてもいいのか、どちらでしょうか?
shibakoppe

2022/05/12 02:18

正直に申し上げれば使う必要性はないと感じております。 リスト1の形からリスト2の形へ変更というか転記をしたいというのが目的なので、本末転倒になってしまいますがVBAでなくても可能な作業だとは思っています。 ボタン一つで自動化するという行為が使用者問わずわかりやすいと感じたため、VBAで作業できたら…という思いで、このコードを使用してみました。
hatena19

2022/05/12 02:55

属性1はA、属性2はB・・ という対応は固定でいいのですか。 シート2の番号は入力済みですか。
shibakoppe

2022/05/12 04:03

>>hatena19様 属性につきましては固定で問題ありません。 また、シート2の番号も入力済みという前提で進めようと思っております。 シート1のリストを蓄積していき、作業が全て完了した後にシート2のリストへの転記をする予定です。
guest

回答4

0

「属性が「1_A_○」や「2_B_○」といった文字列になっている際、「A」や「B」をキー項目として、そのセル丸ごとを転記する」ようにしました。

VBA

1Option Explicit 2'クロス表作成 3Public Sub CommandButton1_Click() 4 Dim dicT As Object 5 Dim keywd As Variant 6 Dim sh1 As Worksheet 7 Dim sh2 As Worksheet 8 Dim maxrow1 As Long 9 Dim row1 As Long 10 Dim row2 As Long 11 Dim key As Variant 12 Dim arr As Object 13 Dim i As Long 14 Set dicT = CreateObject("Scripting.Dictionary") 15 Set sh1 = Worksheets("リスト1") 16 Set sh2 = Worksheets("リスト2") 17 keywd = Array("*A*", "*B*", "*C*", "*D*", "*E*") 18 'リスト2クリア 19 sh2.Rows("2:" & Rows.count).ClearContents 20 maxrow1 = sh1.Cells(Rows.count, "A").End(xlUp).Row 'A列の最大行取得 21 For row1 = 2 To maxrow1 22 key = sh1.Cells(row1, "A").Value 23 If dicT.exists(key) = False Then 24 Set arr = CreateObject("System.Collections.ArrayList") 25 dicT.Add key, arr 26 For i = 0 To UBound(keywd) 27 dicT(key).Add "" 28 Next 29 End If 30 For i = 0 To UBound(keywd) 31 If sh1.Cells(row1, "B").Value Like keywd(i) Then 32 dicT(key)(i) = sh1.Cells(row1, "B").Value 33 Exit For 34 End If 35 Next 36 Next 37 row2 = 2 38 For Each key In dicT.keys 39 sh2.Cells(row2, "A").Value = key 40 For i = 0 To UBound(keywd) 41 sh2.Cells(row2, 2 + i).Value = dicT(key)(i) 42 Next 43 row2 = row2 + 1 44 Next 45 sh2.Range(sh2.Cells(1, 1), sh2.Cells(row2 - 1, UBound(keywd) + 2)).Sort key1:=Range("A1"), Header:=xlYes 46 MsgBox ("完了") 47End Sub 48

別解
System.Collections.ArrayListを使用しない方法です。

VBA

1Option Explicit 2'クロス表作成 3Public Sub CommandButton1_Click() 4 Dim dicT As Object 5 Dim keywd As Variant 6 Dim sh1 As Worksheet 7 Dim sh2 As Worksheet 8 Dim maxrow1 As Long 9 Dim row1 As Long 10 Dim row2 As Long 11 Dim key As Variant 12 Dim arr As Variant 13 Dim i As Long 14 Set dicT = CreateObject("Scripting.Dictionary") 15 Set sh1 = Worksheets("リスト1") 16 Set sh2 = Worksheets("リスト2") 17 keywd = Array("*A*", "*B*", "*C*", "*D*", "*E*") 18 'リスト2クリア 19 sh2.Rows("2:" & Rows.count).ClearContents 20 maxrow1 = sh1.Cells(Rows.count, "A").End(xlUp).Row 'A列の最大行取得 21 For row1 = 2 To maxrow1 22 key = sh1.Cells(row1, "A").Value 23 If dicT.exists(key) = False Then 24 arr = Array("", "", "", "", "", "") 25 dicT.Add key, arr 26 End If 27 arr = dicT(key) 28 For i = 0 To UBound(keywd) 29 If sh1.Cells(row1, "B").Value Like keywd(i) Then 30 arr(i) = sh1.Cells(row1, "B").Value 31 Exit For 32 End If 33 Next 34 dicT(key) = arr 35 Next 36 row2 = 2 37 For Each key In dicT.keys 38 sh2.Cells(row2, "A").Value = key 39 For i = 0 To UBound(keywd) 40 sh2.Cells(row2, 2 + i).Value = dicT(key)(i) 41 Next 42 row2 = row2 + 1 43 Next 44 sh2.Range("A1:F" & row2 - 1).Sort key1:=Range("A1"), Header:=xlYes 45 MsgBox ("完了") 46End Sub

投稿2022/05/12 06:07

編集2022/05/12 07:42
tatsu99

総合スコア5438

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shibakoppe

2022/05/12 06:28

ご回答いただき誠にありがとうございます。 追加の質問にもご対応いただき感謝申し上げます。 実際に記述してみたところ、エラーが起き「Set arr = CreateObject("System.Collections.ArrayList")」この構文の部分で引っかかってしまっているようなのです…。(ポップアップにはオートメーションエラーとありました。) 特に他のアプリを立ち上げているわけでもなく、メモリ的にも問題ないと思うのですが、このような場合の対処法をご教示いただいてもよろしいでしょうか…?
shibakoppe

2022/05/12 08:03 編集

ご丁寧にありがとうございます! 試してみます! 別解も載せていただき誠にありがとうございます◎
guest

0

ベストアンサー

属性につきましては固定で問題ありません。
また、シート2の番号も入力済みという前提で進めようと思っております。

上記の仕様なら、下記のコードでいけます。

vba

1Private Sub CommandButton1_Click() 2 Dim ws1 As Worksheet 3 Set ws1 = Sheets("リスト1") 4 Dim ws2 As Worksheet 5 Set ws2 = Sheets("リスト2") 6 7 Dim i As Long, r As Long, c As Long 8 With ws1 9 For i = 2 To .Range("B" & Rows.Count).End(xlUp).Row 10 r = WorksheetFunction.Match(.Cells(i, 1), ws2.Range("A:A"), 0) 11 c = WorksheetFunction.Match(.Cells(i, 2), Array("A", "B", "C", "D", "E"), 0) + 1 12 ws2.Cells(r, c).Value = .Cells(i, 2) 13 Next 14 End With 15End Sub

存在しない番号や属性値があった場合はエラーになりますので、必要に応じてエラー処理を追加する必要があります。
その場合は下記のコードで。

vba

1Private Sub CommandButton1_Click() 2 Dim ws1 As Worksheet 3 Set ws1 = Sheets("リスト1") 4 Dim ws2 As Worksheet 5 Set ws2 = Sheets("リスト2") 6 7 Dim i As Long, r As Long, c As Long 8 With ws1 9 On Error Resume Next 10 For i = 2 To .Range("B" & Rows.Count).End(xlUp).Row 11 r = WorksheetFunction.Match(.Cells(i, 1), ws2.Range("A:A"), 0) 12 If Err.Number = 0 Then 13 c = WorksheetFunction.Match(.Cells(i, 2), Array("A", "B", "C", "D", "E"), 0) + 1 14 If Err.Number = 0 Then ws2.Cells(r, c).Value = .Cells(i, 2) 15 End If 16 Next 17 On Error GoTo 0 18 End With 19End Sub

追記

コメントにあった属性値を部分一致で検索する場合のコード例です。
WorksheetFunction.Matchを部分一致で検索するユーザー定義関数に置き換えました。

vba

1Private Sub CommandButton1_Click() 2 Dim ws1 As Worksheet 3 Set ws1 = Sheets("リスト1") 4 Dim ws2 As Worksheet 5 Set ws2 = Sheets("リスト2") 6 7 Dim i As Long, r As Long, c As Long 8 With ws1 9 On Error Resume Next 10 For i = 2 To .Range("B" & Rows.Count).End(xlUp).Row 11 r = WorksheetFunction.Match(.Cells(i, 1), ws2.Range("A:A"), 0) 12 If Err.Number = 0 Then 13 c = MyMatch(.Cells(i, 2), Array("A", "B", "C", "D", "E")) 14 If c > 0 Then ws2.Cells(r, c + 1).Value = .Cells(i, 2) 15 End If 16 Next 17 On Error GoTo 0 18 End With 19End Sub 20 21 22Public Function MyMatch(v As String, ary) As Long 23 Dim i As Long 24 For i = 0 To UBound(ary) 25 If v Like "*" & ary(i) & "*" Then 26 MyMatch = i + 1 27 Exit For 28 End If 29 Next 30End Function

投稿2022/05/12 04:26

編集2022/05/12 06:38
hatena19

総合スコア33715

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shibakoppe

2022/05/12 05:30 編集

ご回答いただきまして誠にありがとうございます。 エラー対応時の構文まで載せていただき、とても勉強になりました。 また、こちらで質問させていただくのは失礼かと思ったのですが、以下を併せてお教えいただきたく存じます。 h-okhs様にも同じご質問させていただいたのですが、属性が「1_A_○」や「2_B_○」といった文字列になっている際、「A」や「B」をキー項目として、そのセル丸ごとを転記する際は、質問にも載せさせていただいているIFから始まるコードを使用しても問題ないでしょうか?
shibakoppe

2022/05/12 07:30

追加の質問にもご対応いただきまして誠にありがとうございます。 部分一致のコードでも無事に表を作成することができました。
guest

0

まずDictionaryというデータを入れるオブジェクトにリスト1のデータを詰め込んで、その後、それをリスト2にマッピングする形にしました。

vba

1Sub クロス表() 2 3 Dim dic As Object 4 Set dic = Dictionary生成() 5 Dim lines As Integer 6 lines = リスト2生成(dic) 7 Call ソート(lines) 8 9End Sub 10 11 12Function Dictionary生成() As Object 13 14 Dim dic As Object 15 Set dic = CreateObject("Scripting.Dictionary") 16 17 For i = 2 To Sheets("リスト1").Range("B" & Rows.Count).End(xlUp).row 18 19 Dim 番号 As Integer 20 番号 = Sheets("リスト1").Cells(i, 1).Value 21 Dim 属性 As String 22 属性 = Sheets("リスト1").Cells(i, 2).Value 23 If Not dic.Exists(番号) Then 24 Dim a(5) As Variant 25 dic.Add 番号, a 26 End If 27 Dim 属性リスト As Variant 28 属性リスト = dic.Item(番号) 29 属性リスト(Asc(属性) - Asc("A") + 1) = 属性 30 dic.Item(番号) = 属性リスト 31 Next 32 33 Set Dictionary生成 = dic 34 35End Function 36 37Function リスト2生成(ByRef dic As Object) 38 39 Dim currentRow As Integer 40 currentRow = 2 41 For Each 番号 In dic 42 Sheets("リスト2").Cells(currentRow, 1).Value = 番号 43 For i = 1 To 5 44 Sheets("リスト2").Cells(currentRow, i + 1).Value = dic.Item(番号)(i) 45 Next 46 currentRow = currentRow + 1 47 Next 48 49 リスト2生成 = currentRow - 1 50 51End Function 52 53Sub ソート(ByVal lines As Integer) 54 55 Range("A2:F" & lines).Select 56 Sheets("リスト2").Sort.SortFields.Clear 57 Sheets("リスト2").Sort.SortFields.Add2 Key:=Range("A2:A" & lines), _ 58 SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal 59 With Sheets("リスト2").Sort 60 .SetRange Range("A2:F" & lines) 61 .Header = xlNo 62 .MatchCase = False 63 .Orientation = xlTopToBottom 64 .SortMethod = xlPinYin 65 .Apply 66 End With 67 68End Sub

投稿2022/05/12 04:14

h-okhs

総合スコア149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

h-okhs

2022/05/12 04:27

前提は属性値はA~E固定、シート2は空っぽ(番号未入力)でよい
shibakoppe

2022/05/12 05:16

ご回答いただきまして誠にありがとうございます。 とても助かりました。 こちらで質問させていただくのは失礼かと思ったのですが、以下を併せてお教えいただきたく存じます。 属性が「1_A_○」や「2_B_○」といった文字列になっている際、「A」や「B」をキー項目として、そのセル丸ごとを転記する際は、質問にも載せさせていただいているIFから始まるコードを使用しても問題ないでしょうか?
guest

0

ExcelVBA

1Sub Macro1() 2 Dim rngOld As Range: Set rngOld = Worksheets("Sheet1").Range("A1").CurrentRegion 3 Dim rngNew As Range: Set rngNew = Worksheets("Sheet2").Range("A1") 4 5 Dim pvtCache As PivotCache 6 Dim pvtTable As PivotTable 7 Set pvtCache = ThisWorkbook.PivotCaches.Create(xlDatabase, rngOld) 8 Set pvtTable = pvtCache.CreatePivotTable(rngNew) 9 10 With pvtTable 11 .PivotFields(rngOld(1).Value).Orientation = xlRowField 12 .PivotFields(rngOld(2).Value).Orientation = xlColumnField 13 .PivotFields(rngOld(2).Value).Orientation = xlDataField 14 .ColumnGrand = False 15 End With 16End Sub

こんな感じで。。。。あとは好きなように編集したらよいかと。

投稿2022/05/12 05:28

mattuwan

総合スコア2136

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shibakoppe

2022/05/12 05:46

ご回答いただきありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問