• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:VBA どっちが速い?)

VBAでの配列とコレクションの速度比較

このQ&Aのポイント
  • VBAで要素数1万個の配列を操作する際、要素を削除しながら探す方法を検討しています。
  • 具体的には、商品名が1万個入力されたセルに対して、配列からファイル名を探し、処理を行います。
  • 現在の処理では所要時間が1秒未満ですが、これを短縮するために配列をコレクションに変更する方法を検討しています。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.7

こんにちは。 一応、二分木検索方を知らないわけではありませんが、Excel VBA?で、そのアルゴリズムを使ったことはありませんね。あえてやればできるというだけで、それに代わるものを考えてしまいます。 スピードを比較したわけではありませんから、どちらがどうとは言えませんが、1次元配列で1万個程度なら、ワークシート関数のMatch関数を使うのが速いような気がしますね。Match関数の中身はどうなっているかは知りませんが、二分木(Binary Tree)とは同じ働きをします。それから、Quickソートは、再帰があるから、思ったほどに速くはないはずです。ワークシートのコマンドを利用したほうが速そうです。 >配列をコレクションに変更し、1個処理が終わる度にコレクションの要素から削除していけば、 コレクションというのは、入れ物が大きいオブジェクトですから、数が増えれば、その分重くなるはずです。オブジェクト群を直接操作するなら可能ですが、そうでないと使う意味はあまりありません。 なお、 sName="*" & Cells(i, 4) & ".txt" 'Name -> sName :プロパティ名を直接変数にしてはいけません。 >If Name Like Files(j) then Filter(VBA関数, 戻り値が、見つかららない場合は、-1 になる) Application.Match (ワークシート関数:Application.からダイレクトなのは、エラー値を排除するため。IsError や Vartype : Long で戻り値の有効性を調べる)

over_the_galaxy
質問者

お礼

ありがとうございます。 Matchと他回答のVlookupとあるようですね。両方試してみます。

over_the_galaxy
質問者

補足

体感速くなりました。1個0.6秒程度。後はファイル開閉かも知れません。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (6)

回答No.6

× 配列に入れたファイル名をセルに入れて・・・・ 〇 ファイル名を格納した配列をソートし・・・・ 処理の準備1・・・Excelのセルを参照し配列準備   (1)商品名も配列に。   (2)ファイル名の一部にある商品名を配列に。   (3)ファイル名も配列に。 処理の準備2・・・配列に読み込んだデータをソート 処理の準備3・・・その他の準備 本処理 GoodsCounts = UBound(strGoodsNames()) FileCounts = UBound(strFileNames()) For I=1 TO GoodsCounts  ' ' I番目の商品名の処理 ' strGoodsName=strGoodsNames(I) ' ' 商品名に対応するファイル名を検索 '  ’----------------------- ' バイナリーサーチ  ' -----------------------    ' P=検索開始位置    ' S=二分木探索変数XXX    ' L=二分木探索変数XXX    ' M=二分木探索変数XXX    '    ・・・・・      ・・・・・    ’----------------------- ' ファイル処理  ' -----------------------    ・・・・・      ・・・・・   Next I つまり、EXCEL のセルを参照し配列に読み込むのは準備工程で一回だけです。その後のソートや検索もメモリ上に行います。双方共にソートしておけば、バイナリーサーチの検索ポイントを操作することが可能です。多分、一回の検索に要する時間は1万分の1秒以下だと思いますよ。なお、ファイル処理を上記For-Next文で行うのか、それとも全てを検索後に一括して行うか?これは、後者がお勧めです。まずは、ファイル処理を一括して行うに十分なデータをセルに書き出すか、シーケンシャルファイルとして吐き出すかして成功・不成功を確認。そういうプログラムの単純化がお勧めです。

over_the_galaxy
質問者

お礼

ありがとうございます。 バイナリサーチという機能があるのかと思いましたが、二分木検索の事を指すようですね。

全文を見る
すると、全ての回答が全文表示されます。
  • nishi6
  • ベストアンサー率67% (869/1280)
回答No.5

keyを指定したCollectionオブジェクトを作れば、  「For j=1 to Ubound(Files)    If Name Like Files(j) then の判定がなくなるでしょう。当然、Loopやクイックソートやバイナリーサーチも不要でしょう。Likeの意味がよく分かりませんが、Itemに重複があるということでしょうか。 また、 「For i=2 to Cells(Rows.Count, 1).End(xlUp).Row」 については、セル範囲をVariant型の変数に一括代入すれば、以降はメモリでの処理になり、シートとのI/Oが減ることでこちらは速くなると期待されます。(数十万件の処理で劇的に速くなったこともあります) Collectionにすると、処理速度は若干遅くなるはずで、Variant型の変数との比較はやってみないと分からないと思います。 >配列をコレクションに変更し、1個処理が終わる度にコレクションの要素から削除していけば、探す時間は短縮されると思ったのですが よく試すことがありますが、処理が先頭から順番に行われる場合、逆に末尾から行われる場合。末尾から行われる場合は削除して意味がある? 逆に先頭からの場合は削除にコストがかからないか考える必要があるでしょう。実際のデータを見ないと何とも言えませんね。感覚的にはコレクションならほとんど差がないのではと思います。 実は、質問にある「コレクション」の意味が、「Collectionオブジェクト」なのか「Dictionaryコレクション」なのか判断付きませんでした。個人的には「Dictionaryコレクション」を使っています。当然、ソート不要で削除も可能です(処理効果は分かりませんが)。実際、行ってみるのが一番です。ご参考に。

over_the_galaxy
質問者

お礼

ありがとうございます。 Likeを使う理由はファイル名がまちまちで、商品名を含むのは確実ですが他の部分が一定してないからです。likeならワイルドカードを使えるからです。重複はありません。 コレクションは過去使ったことが無いので、おそらくオブジェクトだと思います。 まずは商品名の配列への代入を試してみます。そういえば一括で変数代入出来たような記憶があります。

全文を見る
すると、全ての回答が全文表示されます。
  • keithin
  • ベストアンサー率66% (5278/7940)
回答No.4

D列に商品一覧を準備 F列にファイル名一覧を(マクロで?その必要があるのかは?)準備 sub macro1()  dim i as long  dim myFile as string  range("F:F").sort key1:=range("F1"), order1:=xlascending, header:=xlno  for i = 2 to range("A65536").end(xlup).row   myfile = application.vlookup("*" & cells(i, "D") & ".txt", range("F:F"),1)  ’ファイル開き処理  next i end sub まぁ騙されたと思って試してみて下さい。 現実にはブックをイチイチ開く、閉じるの方が遥かにボトルネックになりそうですが。 #あぁ、あと実際の具体的なデータの中身とかナイショなので、カンジだけでこのマクロも書いてるので悪しからず。

over_the_galaxy
質問者

お礼

ありがとうございます。 ファイル名をソートしてVlookupするのが肝ですね。エクセル関数をVBA上で使用出来るのは初めて知りました。

全文を見る
すると、全ての回答が全文表示されます。
回答No.3

訂正: ×バイナリソートで探索。 〇バイナリサーチ(二分木探索)で探索。

全文を見る
すると、全ての回答が全文表示されます。
  • maiko0318
  • ベストアンサー率21% (1483/6970)
回答No.2

速くなりますが、もっと早い方法があります。 要素を商品名順にソートしておきます。 であれば、2分木探索が可能です。 <2分木探索> 1万個の真ん中(5000番)と比較。 大きければ前半の前半(2500番)と比較。 ・・・と順にやっていけばめっちゃ速くなります。 プログラムは少し複雑になります。 解説本も売られているかと思います。

over_the_galaxy
質問者

お礼

ありがとうございます。 2分木探索、原理は分かりました。ただ、毎回作ってたら時間が掛かりそうなのでサブプロシージャに分けられたら使えそうですね。

全文を見る
すると、全ての回答が全文表示されます。
回答No.1

Q、速くなるでしょうか? A、速くなると思いますが・・・。 劇的とは言えないと思います。 劇的な改善を実現するには・・・。 1、事前に配列に呼び込む。 2、クイックソートで昇順に並び変える。 3、その後、バイナリソートで探索。 この場合、相当な高速探索ですので削除する必要はありません。

over_the_galaxy
質問者

お礼

ありがとうございます。 配列に入れたファイル名をセルに入れて、昇順にソート、クイックサーチ、でいいのでしょうか?調べてみます。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • ExcelのVBAの配列に関する質問です。

    ExcelのVBAの配列に関する質問です。 sheet1のデータをsheet2に表示するVBAを作成しています。。 sheet1のデータは7行目からスタートし、sheet2のデータは26行目からスタートしています。。 sheet1とsheet2の列は同じ並びではないため、それぞれのシートの列番号をCellsを用いて指定しています。 Sub test1() Dim endrow As Long endrow = Worksheets("sheet1").Range("A65536").End(xlUp).Row Dim i As Long Dim j As Long For i = 7 To endrow j = i + 19 Worksheets("sheet2").Cells(j, 1) = Worksheets("sheet1").Cells(i, 2) Worksheets("sheet2").Cells(j, 8) = Worksheets("sheet1").Cells(i, 28) Worksheets("sheet2").Cells(j, 9) = Worksheets("sheet1").Cells(i, 31) Worksheets("sheet2").Cells(j, 10) = Worksheets("sheet1").Cells(i, 32) ・ ・ ・ ※長いので省略 Next i End Sub 上記のtest1は正常に動くのですが、データ量が多いため、処理に時間がかかってしまいます。 高速化できないかと、以下のように変更しました。 Sub test2() Dim dataRange1 As Variant Dim dataRange2 As Variant dataRange1 = Worksheets("sheet1").Range("A1:GI10006") dataRange2 = Worksheets("sheet2").Range("A1:DZ10018") Dim endrow As Long endrow = Worksheets("sheet1").Range("A65536").End(xlUp).Row Dim i As Long Dim j As Long For i = 7 To endrow j = i + 19 dataRange2(j, 1) = dataRange1(i, 2) dataRange2(j, 8) = dataRange1(i, 28) dataRange2(j, 9) = dataRange1(i, 31) dataRange2(j, 10) = dataRange1(i, 32) ・ ・ ・ ※長いので省略 Next i End Sub test2は、エラーメッセージ等は表示されませんが、sheet2にデータが表示されません。 ちなみに、以下のようにsheet1のみ配列化した場合は、正常に表示されました。 Worksheets("sheet2").Cells(j, 1) = dataRange1(i, 2) Worksheets("sheet2").Cells(j, 8) = dataRange1(i, 28) Worksheets("sheet2").Cells(j, 9) = dataRange1(i, 31) Worksheets("sheet2").Cells(j, 10) = dataRange1(i, 32) test2は、どこが間違っているのでしょうか? ご教示ください。 よろしくお願いいたします。

  • エクセルVBAの繰り返し処理の質問

    C列にある項目とG列にある項目を比較して、 一致し、H列にある数字が10以上ならば、B列にフラグ1を立てる という処理を行いたいんですが、 下記ぐらいまでしか作れず、うまくいきません・・・ Sub フラグを立てる処理() Dim i As Integer Dim j As Integer Dim k As Integer i = 1 j = 1 Do j = j + 1 Do i = i + 1 If Cells(j, 8) > 9 Then Cells(i - 1, 4) = 1 End If Loop Until Cells(i, 3) <> Cells(j, 7) Or Cells(i, 3) = "" Loop Until Cells(j, 7) = "" End Sub わかる方がいらっしゃいましたら、お願いします。

  • (VBA) 配列の文字列を昇順で並べ替えたい

    タイトルの通り、配列に格納したファイル名を昇順に並べ替えたいのですが、期待通りに動作しません。 内部コード(ユニコード?)順には並んでいるようですが、エクスプローラの名前順と同等にはなりません。 どのようにしたら、配列のファイル名をエクスプローラと同じように並べ替えできますか? (テストに使用したコード) Public Sub Test1() Dim FileNames() As String Dim WSH As Object Dim MyPathName As String Dim MyFileName As String Dim i As Integer Dim j As Integer '処理対象フォルダを指定 Set WSH = CreateObject("WScript.Shell") MyPathName = WSH.SpecialFolders("MyDocuments") Set WSH = Nothing i = 0 MyFileName = Dir(MyPathName & "\" & "*.*") If MyFileName = "" Then MsgBox "対象ファイルが1つも見つかりません。", , "処理終了" Exit Sub End If 'ファイル一覧を配列に格納 Do Until MyFileName = "" i = i + 1 ReDim Preserve FileNames(1 To i) FileNames(i) = MyFileName MyFileName = Dir Loop '配列を並べ替える For i = 1 To UBound(FileNames) - 1 For j = i To UBound(FileNames) If FileNames(i) > FileNames(j) Then MyFileName = FileNames(i) FileNames(i) = FileNames(j) FileNames(j) = MyFileName End If Next j Next i End Sub ※以降の処理は、Excel で処理するか Access で処理するかまだ決めていません。  (投稿文字数の関係で詳細は省略)

  • VBAでの質問

    お世話になります。 下記の記述で、「←」の矢印の記述で、 Cells.(5,2)がブランクでなければ、 「→」から進めたいのですが、 どの様に記述すれば宜しいでしょうか ご教示お願いします。 Sub 表記入() Dim Data As Range Dim i As Integer Dim j As Integer Dim k As Integer Set Data = Sheets("集計").Range("A2").CurrentRegion j = 16 k = 0 With Sheets("表") For i = 3 To Data.Rows.Count If Data.Cells(i, 51) <> "" Then .Cells(5, 2) = Data.Cells(i, 3)  ← .Cells(5, 7) = Data.Cells(i, 4) → .Cells(j, 2) = Data.Cells(i, 10) .Cells(j, 6) = Data.Cells(i, 11) & Data.Cells(i, 12) .Cells(j, 14) = Data.Cells(i, 51) Else End If k = k + 1 If k = 10 Then j = j + 18 k = 0 Else j = j + 1 End If End If Next i End With End Sub

  • ExcelのVBAで質問です。

    以下のようなシートがあります。 A列  B列  C列  D列  E列  F列  G列  H列 NO  名前  確認 日付1 日付2  確認 日付1 日付2 6行目からデータを入れる予定です。 C列には○印を入力します。 C列~E列までデータが入った行は、 F列~H列まで同じ処理をします。 (セルの背景色を変えます。) 以後同じことを列方向で繰り返します。 以下のようなVBAを組みましたが、 ○の個数を数える部分でエラーがでます。 Private Sub Worksheet_Change(ByVal Target As Range) Dim staff As String Dim kakunin As String Dim date1 As Date Dim date2 As Date Dim i As Long Dim j As Long Dim cnt As Long '100件分ループ For i = 6 To 105 '○の数をカウント cnt = WorksheetFunction.CountIf(ActiveSheet.Range(Cells(i, 2), Cells(i, 256)), "○") 'jとは確認の列番号のこと j = 3 * cnt staff = Cells(i, 2) kakunin = Cells(i, j) date1 = Cells(i, j + 1) date2 = Cells(i, j + 2) 'スタッフ名が入力されたら If staff = "" Then Range(Cells(i, j), Cells(i, j + 50)).Interior.ColorIndex = 15 Else Cells(i, j).Interior.ColorIndex = xlNone '○が入力されたら If kakunin = "○" Then Range(Cells(i, j + 1), Cells(i, j + 2)).Interior.ColorIndex = xlNone Else Range(Cells(i, j + 1), Cells(i, j + 2)).Interior.ColorIndex = 15 End If If Cells(i, j + 1) <> "" And Cells(i, j + 2) <> "" Then Cells(i, j + 3).Interior.ColorIndex = xlNone End If End If Next i End Sub ご教授いただけたら、幸いです。 よろしくお願いいたします。

  • VBA Dowhile 判断条件に動的配列を使う

    Dim i As Long Dim A() As String '何かの処理で配列全部に文字列を入れる。 i=1 Do while Len(A(i))>0 '何かの処理 i=i+1 Loop 配列の全部の要素に文字列が入ってると、最後のiでエラーになります。静的配列を使う場合は、要素数に余裕を持たせて宣言するので問題無かったのですが、動的配列を使うようになりエラーが出ました。 動的配列では条件判断に配列を使わない方がいいのでしょうか?というよりDowhile自体使わない方がいいのでしょうか。 勿論やりようで使える筈です。例えば要素数を1個追加して空""にするとか。常識的な方法はあるのでしょうか。例えばFor nextとか。

  • エクセル2007 VBA シート内のデータを項目名で検索し、その列を新

    エクセル2007 VBA シート内のデータを項目名で検索し、その列を新規シートにコピーする方法についてです。 VBAについては初心者で、グーグルで調べながら作ったのですが、コピー後のペーストが上手く出来ません。どうすれば最後まで処理できるのかを教えて下さい。 それと、全体的に書き方がおかしいところがありましたら指摘・改善方法を教えて下さい。 よろしくお願いします。 Sub 配列並べ替え() Dim myArray As Variant '1項目名希望順配列格納 Dim strArray As Variant '2検索用1の配列格納 Dim LastCol1 As Long '3最終列数格納 Dim LastCol2 As Long '4新規シートの最終列数格納 Dim DefSheetname As Variant '5初期のシート名取得 Dim i As Long Dim j As Long '初期シート名を取得。 DefSheetname = ActiveSheet.Name '初期シートの最終列数取得。 LastCol1 = Worksheets(DefSheetname).Range("A1").End(xlToRight).Column 'シート名:レポートの新規シート追加。 Worksheets.Add.Name = "レポート" '初期シートを選択。 Worksheets(DefSheetname).Select '項目名希望順配列格納。 myArray = Array("得意先C", "取引先名1", "製番", "相手管理NO", "品目C", _ "製品名1", "受注数", "受注残数", "納期", "受注単価", _ "受注金額", "出荷数", "出荷金額", "出荷先名1", "郵便番号", "住所1", "TEL", "FAX") '配列要素数分繰り返し処理。 For i = LBound(myArray) To UBound(myArray) '検索用の配列(項目名)格納。 strArray = myArray(i) 'A1:LastCol1範囲で配列(項目名)検索し、番号で返す。 j = WorksheetFunction.Match(strArray, Worksheets(DefSheetname).Range(Cells(1, 1), Cells(1, LastCol1)), 0) 'シート名:レポートに変数jの列数目の値を入力。 Columns(j).Copy 'シート名:レポートの最終列数取得。 LastCol2 = Worksheets("レポート").Range("A1").End(xlToRight).Column 'シート名:レポートを選択。 Worksheets("レポート").Select Range(Cells(1, 1), Cells(1, "LastCol2")).Past Next i End Sub

  • VBAでSplitエラーです

    環境:Excel2002です   Cells(1, 1)に(1)~(10),(13),(20)~(28)のような値があります   Cells(1, 1)の値は別のプロシージャ求めていてその都度変わります   Cells(j, 4)をスタート位置にして   Cells(j, 4)に(1)~(10)   Cells(j+1, 4)に(13)   Cells(j+2, 4)に(20)~(28)を表示したいので   以下のプロシージャにしました Dim Str As String Str = ActiveSheet.Cells(1, 1).Value Dim i As Integer Dim j As Integer '◆カンマで区切った文字列をD列に格納 Dim tmp As Variant tmp = Split(Str, ",") j = 1 For i = 0 To UBound(tmp) ActiveSheet.Cells(j, 4) = tmp(i) j = j + 1 Next i   あるブックでは正常に動作するのですが   別のブックでは以下のエラーメッセージがでて動作しません   【モジュールではなく、変数またはプロシージャを指定してください】   何が原因なのでしょうか?ご教示願います   Splitを使わない別の方法があればそれでも結構です   とにかく困っています

  • VBAの動的配列について

    いつもお世話になっております。 エクセルVBAを学習中の者です。 動的配列についてお伺いします。 添付資料を見て頂きたいのですが、 シート名1~4に同一レイアウトの表があります。 これらの表をを2次元配列に格納し、その後、同一レイアウトのシートに一括転記したいと考えています。 転記の事を考えて、条件としては、 シート1から2行目以降のデータを配列『data』に格納、変数『dataCnt』が転記先の行番号と同じになるように考えています。 当初は、配列の定義を『Dim data(100,3) As Variant』と、多めに要素数を定義して、コードを記述していました。 正直、凄く気持ちが悪い感じでした・・・ 最近、動的配列を学習しまして、 シートごとにデータの行数を変数『lastRow』に格納して、配列を再定義して【データ数=要素数】とならないか? と思い、下記のようなコードを書いてみました。 が、『ReDim Preserve~』で実行エラーが発生してしまいます。 原因がなぜかわかりません! そもそも、動的配列はこのような使い方は出来ないのでしょうか? Sub テスト() Dim data() As Variant Dim x As Long Dim i As Long Dim ii As Long Dim lastRow As Long Dim dataCnt As Long dataCnt = 2 For x = 2 To 5 Worksheets(x).Activate lastRow = Cells(Rows.Count, 1).End(xlUp).Row If x = 2 Then ReDim data(2 To lastRow, 3) Else ReDim Preserve data(2 To dataCnt + lastRow - 1, 3) End If For i = 2 To lastRow For ii = 1 To 3 data(dataCnt, ii) = Cells(i, ii) Next ii dataCnt = dataCnt + 1 Next i Next x End Sub どなたかご指導をよろしくお願いいたします。

  • VBA フォルダ内のファイルを、ファイル名順に開く

    【PPT VBA】フォルダ内のファイルを、ファイル名順に開く方法について  今晩は、質問させていただきます。どうぞよろしくお願いいたします。  環境:Win7+PPT2007 になります。  下記のようなコードで、あるフォルダ内のファイル群のデータを取得しております。 「ファイル名順」でデータを取り出したいのですが、違う順(おそらく日時順)で 出てくるので悩んでいる次第でございます。  検索いたしておりますと、ハードディスク等の環境によって検出順が異なるようでございましたが 何か、良さそうな方法がございましたらお教えいただきたくお願いいたします。 Dim File_Collection As Object Dim File_List As Variant Dim Folder_Collection As Object Dim Folder_List As Variant Set File_Collection = CreateObject("Scripting.FileSystemObject") _ .GetFolder(パス).Files For Each File_List In File_Collection      '(名前順では検出してくれません。。。) Next  暫く考えまして、例えば Stringの配列を用意しておいて一旦、上記コードでファイル名とパスをリスト化 →配列の文字列を登り順に、Sort →これの順にファイルを開いていく などという事を思いつきました。しかしファイル数が多いため(100~500個でございます)、 これらの長いパスを配列に入れるというのも、メモリに負担がかかる(?)のかな などと考えている次第でございます。  他に、配列の代わりにエクセルを一旦開いて、そこでソート・・・・・とも考えましたが 友人達に使っていただく可能性があり、余りスマートな解決策ではなさそうですので それよりは上記の「配列でソート」を優先したい次第でございます。  コーディングで何か良い方法があれば良いのでございますが、 他の方法でも結構でございます。もし「自分なら、こうするかな」といった ようなご意見などございましたら是非、ご紹介いただけないでしょうか。  もしお詳しい方がいらっしゃいましたら,どうぞよろしくお願いいたします。

専門家に質問してみよう