こんにちは。プログラミング初心者です。
VBAで質問です。
以下のようなシートがあるとします。
A | B | C | D | E | F
1 シート選択 | (プルダウンリストで選択します)
2 果物シート | りんご | みかん | ばなな
3 教科シート | 国語 | 算数 | 理科 | 社会 | 図工 | 体育
4 乗物シート | 車 | 電車 | 船 | 飛行機
そして、以下のようにB1セルのプルダウンで選択されたシート名をA列で検索してヒットした行の文字列を配列に格納します。
selectedSheet = cells(1, 2).Value
for i = 2 To 4
if cells(i , 1).Value = selectedSheet Then
rightRow = Cells(i, Columns.Count).End(xlToLeft).Column
Redim rowArray(rightRow - 1)
for j = 1 To rightRow
rowArray(j - 1) = cells(i, j).Value
Next j
Exit For
End if
next i
次に、配列に格納された文字列をB1のプルダウンで選択されたシートの1行目で、配列の各要素を検索して、それぞれが何列目にあるかを調べます。
sheetName = rowArray(0)
sheetObject = Worksheets(sheetName)
lastColumn = sheetObject.Cells(1, Columns.Count).End(xlToLeft).Column
Set searchRange = sheetObject.Range((cells(1, 1), cells(1, lastColumn))
col_01 = Find(rowArray(1), searchRange) 'りんごを検索してます
col_02 = Find(rowArray(2), searchRange) 'みかんを検索してます
col_03 = Find(rowArray(3), searchRange) 'バナナを検索してます
その後、これで得られた列情報をもとにいろいろと処理をするのですが、上記のように「col_01 = ***」のような書き方ができるのは、あらかじめ「果物シート」がセラばれることが分かっている場合のみです。
「教科シート」が選択された場合は、「col_**」の「**」の部分が「6」まである必要がありますし、「乗物シート」の場合は変数が4つになります。
このように必要となる変数の数が動的に変化する場合に変化する場合、どのように処理したらよいでしょうか。
最初の「for i = 2 To 4」のループとのころで、ヒットしたシート名によって、それぞれ別のSubやFunctionを呼ぶというのは避けたいです。
というのも、VBAをまったく知らないユーザでも上記テーブルの「果物シート」行に「メロン」を足したり、「乗物シート」の「飛行機」を削除することで、処理対象列を自由にカスタマイズできるようにしてあげたいのです。
(あるいは、乗物シートの下に「肩書シート | 社長 | 部長| 係長」のような行を挿入して、処理対象のシートそのものを増やしたり)
どなたか、よい方法をご存知でしたら、教えていただけないでしょうか。
よろしくお願いいたします。
No.4ベストアンサー
- 回答日時:
途中から失礼します。
結局、
Sub try()
Dim s As String
Dim r As Range
Dim n As Long
Dim i As Long
Dim x, y, z
With ActiveSheet
s = .Cells(1, 2).Value
x = Application.Match(s, .Columns(1), 0)
n = .Cells(x, .Columns.Count).End(xlToLeft).Column - 1
If n = 0 Then MsgBox "no data": Exit Sub
y = Application.Transpose(.Cells(x, 2).Resize(, n))
y = Application.Transpose(y)
End With
With Sheets(s)
z = Application.Match(y, .Rows(1), 0)
If n = 1 Then
If IsNumeric(z) Then
Set r = .Columns(z)
End If
Else
For i = 1 To UBound(z)
If IsNumeric(z(i)) Then
If r Is Nothing Then
Set r = .Columns(z(i))
Else
Set r = Union(r, .Columns(z(i)))
End If
End If
Next
End If
End With
If Not r Is Nothing Then
r.Copy Worksheets.Add.Range("A1")
Set r = Nothing
End If
End Sub
こういう事なんでしょうか。
列位置を変数にとって、必要な列だけを別シートに抜き出すという処理。
行方向のLoopに条件分岐処理がないなら列ごとコピーで良さそうですが。
上記例みたく。
でもそういう処理の場合で、抜き出す項目名が必ず元データにある、
という事が保証されているならAdvancedFilterメソッドが簡単です。
Sub try_2()
Dim s As String
Dim n As Long
Dim x
With ActiveSheet
s = .Cells(1, 2).Value
x = Application.Match(s, .Columns(1), 0)
n = .Cells(x, .Columns.Count).End(xlToLeft).Column - 1
If n = 0 Then MsgBox "no data": Exit Sub
.Cells(x, 2).Resize(, n).Copy Worksheets.Add.Range("A1")
End With
Sheets(s).Range("A1").CurrentRegion.AdvancedFilter Action:=xlFilterCopy, _
CopyToRange:=Range("A1").Resize(, n), Unique:=False
End Sub
もしLoop処理が必要な場合でも、1セルずつ書き出しているから遅いんであって、
一旦、配列に入れて、書き出しはまとめて1回で済ますようにすれば速度的にも改善します。
ご回答ありがとうございます!
すごい!いろいろ勉強させていただきました。
Matchの検索対象に2次配列が指定できること。そしてその結果を1次配列で受け取れること。
Unionで連続していない列を1つのオブジェクトとして扱えるようになるというのも目から鱗です。
ちなみに、以下のようなシートがあったとして、key列とその他2列を抽出(コピー)する場合は、key列をA列に、その他2列は2つの空白列のに続いて、D列とE列にコピー。
key列とその他3列を抽出(コピー)する場合は、key列はA列に、その他3列は3つの空白列に続いて、E、F、G列にコピーなんてことができるでしょうか?
それともやはり、key列のコピーとその他列の処理を2回に分けてやるしかないでしょうか。
key | りんご | 西瓜 | みかん | バナナ
1 | test | test | test | test
2 | sample | sample | sample | sample
3 | dummy | dummy | dummy | dummy
じつは、コピーしたkey列の左にある空白列に、同名の日付の古いファイルから同じ列名のデータを引っ張ってきて(行が追加/削除されている可能性もあるので、keyの数字を検索キーにVLOOKUPか何かでやる予定)、差分を取りたいのです。
なんだか、お礼というより追加の質問みたいになってしまって恐縮ですが、
そちらの方も余裕がありましたら、お知恵を拝借できないでしょうか。
【key列と、りんご列・みかん列を抽出した際の結果】
key | | | りんご | みかん
1 | | | test | test
2 | | | sample | sample
3 | | | dummy | dummy
No.6
- 回答日時:
一応、AdvancedFilterメソッドを使った場合のサンプル。
『抜き出す項目名が必ず元データにある、という事が保証』
されてなくても使えるように存在チェックをすれば良いですね。
Sub try_2_kai()
Dim s As String
Dim n As Long
Dim i As Long
Dim c As Long
Dim x, y, z
'ActiveSheetのB1セル値を検索してその右列データを取得
With ActiveSheet
s = .Range("B1").Value
x = Application.Match(s, .Columns(1), 0)
If IsError(x) Then MsgBox "error": Exit Sub
n = .Cells(x, .Columns.Count).End(xlToLeft).Column - 1
If n = 0 Then MsgBox "no data": Exit Sub
y = .Cells(x, 2).Resize(, n).Value
End With
'取得データが元データ項目になければ除外
x = Application.Match(y, Sheets(s).Rows(1), 0)
ReDim z(1 To n)
If n = 1 Then
If IsNumeric(x) Then
c = 1
z(c) = y
End If
Else
For i = 1 To n
If IsNumeric(x(i)) Then
c = c + 1
z(c) = y(1, i)
End If
Next
End If
'新規Sheetに除外後データをセットしてAdvancedFilter
If c = 0 Then MsgBox "no data": Exit Sub
With Sheets.Add.Range("A1").Resize(, c)
.Value = z
Sheets(s).Range("A1").CurrentRegion.AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=.Cells
If c > 1 Then
.Item(2).Resize(, c - 1).EntireColumn.Insert
End If
End With
End Sub
ご丁寧にありがとうございます!
じつは、昨日の今頃AdvancedFilterで任意の列だけxlFilterCopyするにはどうしたらいいんだろうと、試行錯誤していたのですが、以下の部分の「With Sheets.Add.Range("A1").Resize(, c)」のようにコピー先の範囲を指定してあげればよかったんですね。
With Sheets.Add.Range("A1").Resize(, c)
.Value = z
Sheets(s).Range("A1").CurrentRegion.AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=.Cells
以下の部分のようにヒットした列番号をソートする個所といい、自分じゃ絶対思いつかなかったですね。今後これを自分の引き出しに入れておこうと思います。
ありがとございました。勉強になりました。
If IsNumeric(x(i)) Then
c = c + 1
z(c) = y(1, i)
End If
No.5
- 回答日時:
>それともやはり、key列のコピーとその他列の処理を2回に分けてやるしかないでしょうか。
別にUnionメソッドを使って一括でやらなくても、
1列ずつコピーしても良いかと思います。
各行Loopに比べれば。
>じつは、コピーしたkey列の左にある空白列に、
>同名の日付の古いファイルから同じ列名のデータを引っ張ってきて
>(行が追加/削除されている可能性もあるので、
>keyの数字を検索キーにVLOOKUPか何かでやる予定)、差分を取りたいのです。
この処理はコピーの後に行うんですよね。
コピー後に空白列を設けるという事で良ければ
単純にコピー後、列挿入ではいけませんか。
> コピー後に空白列を設けるという事で良ければ単純にコピー後、列挿入ではいけませんか。
おっしゃるとおりですね。
仕様上そうすうわけにいかない事情があったのですが、仕様自体を変更してそういう処理にしようと思います。
No.3
- 回答日時:
変数をcol_01とかcol_02とかしないといけないという考え方がずれています。
後の処理がどういう処理なのかわかりませんが、どうしても個々に変数に入れたいのであれば、
以下の様にすればよろしいかと思います。
ReDim col(UBound(rowArray))
For i = 0 To UBound(rowArray)
col(i) = Find(rowArray(i), searchRange).Column
Next i
たびたびのご回答ありがとうございます。
やっぱり、そうするしかないですよね。
実は、自分で書いた現状のコードがそういうかたちになってます。
この後、「果物シート」とか「乗物シート」の2行目から最終行までForでループしつつ、col()の配列を読み込むループをネストして処理していくことになるのですが、本番データは平均して数千行あるために処理に時間がかかってしまいます。
【現状を単純化したイメージ】
for i = 2 to lastRow
for j = 0 to elementNum
if j = 0 then
colNum = 1
cells(i, colNum).value = 果物シート.cells(i, j).value
else
cells(i, colNum + j).value = 果物シート.cells(i, j).value
end if
next j
next i
※ i のループが1000、jのループが3として3000ステップ (+if分岐)
これを以下のように、できればかなり処理速度が改善されるはずだと思ったわけです。
(col()の要素が3つなら単純に1/3になるはず)
【改善案のイメージ】
for i = 2 to lastRow
cells(i, colNum1).value = 果物シート.cells(i, col_01).value
cells(i, colNum2).value = 果物シート.cells(i, col_02).value
cells(i, colNum3).value = 果物シート.cells(i, col_03).value
next i
※ i のループの1000ステップのみ
PHPでCSVの任意のカラム(複数)を読み込む処理をググってみても、やっぱりだいたいforのループにfor each をネストする形になっていますし、そういうものなのかもしれませんね(PHPだとそれほど処理が遅いとも思いませんが…サーバの性能がいいだけかもしれません)。
No.2
- 回答日時:
配列の最大数を取得するには、UBoundを使います。
後の処理がどういう処理なのか不明ですが、変数はcolの1つだけにしておいて、
以下のような感じにされてはどうでしょうか
Sub test()
Dim I As Integer
For I = 1 To UBound(rowArray)
col = Find(rowArray(I), searchRange)
'後の処理
Next I
End Sub
この回答への補足
ご回答ありがとうございます。
説明が分かりづらくて申し訳ありません。
冒頭に示したシートとは別に「果物シート」「乗物シート」があるという想定です。
(本番データはもっとマジメな業務用のデータですが…)
冒頭で示したシートは、それらのシートのどの列の情報を取得するかという定義用のものなのです。
(ここをVBAを知らない一般のユーザがいじることで自由にツールをカスタマイズできるという意図)
例)果物シート
A | B | C | D | E | F
1 りんご | みかん | ばなな | メロン | スイカ | トマト?
2 オフシーズン | ハウス物 | 輸入物 | 高級品 | 夏物 | 露地物
3 値上がり | 値上がり | 同じ | いつも高い | N/A | 値上がり
4 赤 | オレンジ | 黄色 | 黄緑 | 深緑 | 赤
さて、質問冒頭で示したシートで「果物シート」が選択されると、Uboud(rowArray)が2で、+1すれば要素数が判ります。
更に、配列内に格納された列名を「果物シート内」の1行目で検索すると、リンゴ列、ミカン列、バナナ列は、それぞれ1列目、2列目、3列目にあることがわかります。これが、質問の最後に示したい以下の部分です(Find(rowArray(*), searchRange).columnとすべきでしたが)。
col_01 = Find(rowArray(1), searchRange) 'りんごを検索してます
col_02 = Find(rowArray(2), searchRange) 'みかんを検索してます
col_03 = Find(rowArray(3), searchRange) 'バナナを検索してます
ここで、あらかじめ「果物シート」が選択されることがわかっていれば、col_01、col_02、col_03と3つの変数を宣言しておいて、処理対象列の番号を格納しておけばいいのですが(例:columns(col_01)のように使う予定です)、「教科シート」なら6つ(col_01~col_06)、「乗物シート」なら4つの変数を宣言しておく必要があります。
本番データでは、1列目、2列目、3列目なんて連続していることはありませんし、処理対象とする列数も2~10の間で変化します。
また、配列に格納された列名を処理対象シートの1行目で検索し、列番号をcol_0*に格納する以下のような処理も、あらかじめ「果物シート」が選択されることがわかっているときのみ有効です。
for i = 0 To Ubound(rowArray)
if i = 0 then
col_01 = Find(rowArray(i), searchRange).Column
elseif i = 1 then
col_02 = Find(rowArray(i), searchRange).Column
elseif i = 2 then
col_03 = Find(rowArray(i), searchRange).Column
next i
このあたりの処理で、冒頭のテーブルB2で「教科シート」や「乗物シート」が選ばれたときにも、柔軟に対応できるようにするには、どうしたらよいでしょうか。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) まとめシートから集計シートへA列のコードが一致したら1行コピーするマクロをネット上で見つけました。こ 1 2022/08/30 14:11
- Visual Basic(VBA) VBAで、シート間の転記するコードをFOR~NEXTで教えてください。 9 2023/04/30 20:04
- Visual Basic(VBA) ユーザーフォーム「frm_基本❶」を立ち上げると新規で入力する行数を右下のNoとして表示しています。 1 2023/03/16 19:02
- Visual Basic(VBA) コード名シートA列と集計シートA列のコードが一致したら、コード名シートA5からk12の範囲をコピーし 1 2022/08/29 23:46
- Visual Basic(VBA) エクセルのマクロで対象ごとにシート分けしてその内容をセルに書き込みたい 9 2022/08/24 13:23
- Visual Basic(VBA) 列と行の名前(重複あり)が交差するセルに、データを入力したい 2 2022/06/25 22:42
- Visual Basic(VBA) VBA Userformで一部別シートに転記がしたいのですが 2 2023/05/24 13:08
- Visual Basic(VBA) ファイル全てを .xlsm に変更したところ、プログラムが途中で落ちてしまっています 17 2022/12/07 12:03
- Visual Basic(VBA) VBAでのループ順序について 3 2023/03/13 10:55
- Excel(エクセル) VBAについて 3 2022/06/19 18:19
このQ&Aを見た人はこんなQ&Aも見ています
-
カンパ〜イ!←最初の1杯目、なに頼む?
飲み会で最初に頼む1杯、自由に頼むとしたら何を頼みますか? 最初はビールという縛りは無しにして、好きなものを飲むとしたら何を飲みたいですか。
-
人生最悪の忘れ物
今までの人生での「最悪の忘れ物」を教えてください。 私の「最悪の忘れ物」は「財布」です。
-
忘れられない激○○料理
これまでに食べたもののなかで、もっとも「激○○」だった料理を教えて下さい。 激辛、でも激甘でも。 激ウマ、でも激マズでも。
-
プリン+醤油=ウニみたいな組み合わせメニューを教えて!
プリンと醤油を一緒に食べると「ウニ」の味がする! というような意外な組み合わせから、新しい味になる食べ物って色々ありますよね。 あなたがこれまでに試した「組み合わせメニュー」を教えてください。
-
タイムマシーンがあったら、過去と未来どちらに行く?
20XX年、ついにタイムマシーンが開発されました。 あなたは過去に行く? それとも未来? タイムマシーンにのって、どこに行って、何をしたいか教えてください!
-
VBAでループ内で使う変数名を可変にできないか。
Visual Basic(VBA)
-
VBA 変数名に変数を使用したい。
Visual Basic(VBA)
-
変数を動的に作るには?
Visual Basic(VBA)
-
-
4
Excel-vba 文字列と変数を連結して更に変数として扱いたい
その他(プログラミング・Web制作)
-
5
配列をループでたくさん宣言したいのですが、配列名や変数名を変数で宣言することはできませんか?
Visual Basic(VBA)
-
6
配列を使わずに、変数名を動的にループで回したい
C言語・C++・C#
-
7
エクセルのラベルの値(文字列)を垂直方向で中央揃えにするには?
Excel(エクセル)
-
8
ユーザーフォームに入力したデータを保持する方法
Visual Basic(VBA)
-
9
VBAで配列の計算
Excel(エクセル)
-
10
Excel、VBAのユーザーフォームのラベルで変数を…
Excel(エクセル)
-
11
ユーザーフォームを表示中にシートの操作をさせるには
Excel(エクセル)
-
12
2つ目のレコードの値を取得するには?
Access(アクセス)
-
13
VBA:小数点以下の数字を取得できる関数は?
Visual Basic(VBA)
-
14
VBAで文字列を数値に変換したい
Excel(エクセル)
-
15
Application.ScreenUpdating = Falseが効きません
Visual Basic(VBA)
-
16
エクセルでアルファベットか数値の判定をしたいのですが
Excel(エクセル)
-
17
worksheetFunctionクラスのVlookupプロパティを取得できません エラーへの対応
Visual Basic(VBA)
-
18
EXCELで特定のセルに表示された項目をヘッダーやフッターに出力するには
Excel(エクセル)
-
19
ファイルは既に開かれています(Error55)について
Visual Basic(VBA)
-
20
VBAでユーザーフォームを再表示させたい。
Excel(エクセル)
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【大喜利】【投稿~11/22】このサンタクロースは偽物だと気付いた理由とは?
- ・お風呂の温度、何℃にしてますか?
- ・とっておきの「まかない飯」を教えて下さい!
- ・2024年のうちにやっておきたいこと、ここで宣言しませんか?
- ・いけず言葉しりとり
- ・土曜の昼、学校帰りの昼メシの思い出
- ・忘れられない激○○料理
- ・あなたにとってのゴールデンタイムはいつですか?
- ・とっておきの「夜食」教えて下さい
- ・これまでで一番「情けなかったとき」はいつですか?
- ・プリン+醤油=ウニみたいな組み合わせメニューを教えて!
- ・タイムマシーンがあったら、過去と未来どちらに行く?
- ・遅刻の「言い訳」選手権
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
マクロの「SaveAs」でエラーが...
-
EXCELのSheet番号って変更でき...
-
マクロ実行後に別シートの残像...
-
Changeイベントで複数セルへの...
-
VBAで変数の数/変数名を動的に...
-
Count Ifのセルの範囲指定に変...
-
ExcelのVBマクロを、バックグラ...
-
RemoveDuplicatesメソッドにつ...
-
グラフマクロで系列を変数にす...
-
VBA 別ブックからの転記の高速...
-
100万件越えCSVから条件を満た...
-
2010 Excel VBA 測定結果を貼...
-
VBA 重複チェック後に値をワー...
-
GASでチェックボックスを一括of...
-
VBAでのピボットテーブルの範囲...
-
エクセル マクロ VBA Range Val...
-
テキストボックスから、複数の...
-
VBA 空白行に転記する
-
ExcelのVBA ListBox.RowSource...
-
FindNextがうまくいかない
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
マクロの「SaveAs」でエラーが...
-
EXCELのSheet番号って変更でき...
-
VBA 空白行に転記する
-
マクロ実行後に別シートの残像...
-
VBA別シートの最終行の次行へ転...
-
Changeイベントで複数セルへの...
-
Count Ifのセルの範囲指定に変...
-
ExcelのVBマクロを、バックグラ...
-
VBA 実行時エラー1004 rangeメ...
-
VBAで変数の数/変数名を動的に...
-
VBA 別ブックからの転記の高速...
-
Excel VBA オートフィルターで...
-
100万件越えCSVから条件を満た...
-
複数シートの複数列に入力され...
-
【VBA】特定の条件でセルをコピー
-
Excel2013で切り取り禁止
-
楽天RSSからエクセルVBAを使用...
-
アクセスからエクセルへ出力時...
-
グラフマクロで系列を変数にす...
-
FindNextがうまくいかない
おすすめ情報