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

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

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

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

フィルタ

フィルタとは、特定の条件に合わせてデータへのアクセスをブロックするプログラムやルーチンを指します。

マクロ

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

Q&A

解決済

1回答

7193閲覧

Excel VBA フィルターをかけた際に表示されている値だけを配列に格納する実装

kuta

総合スコア10

VBA

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

フィルタ

フィルタとは、特定の条件に合わせてデータへのアクセスをブロックするプログラムやルーチンを指します。

マクロ

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

0グッド

2クリップ

投稿2020/03/02 02:44

編集2020/03/02 02:47

前提・実現したいこと

ExcelVBAのフィルターと配列についてご質問をさせてください。
下記はサンプルデータになります。
イメージ説明

列が2列あり、複数の値がそれぞれ記載されています。
1.1列目のデータを重複無しで配列に格納
2.配列1つ目の値でフィルターを設定
3.フィルター後、2列目に現在表示されている値だけを別の配列に格納
という処理を行いたいです。
例えば上記画像ですと、A列の値1でフィルターをかけ、B列の表示されている値(A,A,B)のみを配列に入れたいです。
3.のフィルター後に表示されている値「だけ」を配列に格納するという手段がわからず、お教え頂きたいです。
また、データ行が30000行程あり、なるべく高速な処理を行いたいです。

発生している問題・エラーメッセージ

該当のソースコード

言語:VBA

※現状は下記ソースで重複無しのデータを配列に取得しているのですが、
こちらだとフィルター後に取得してもすべての値を取得してしまうため、ご質問させて頂きました。

Sub Duplicate_Delete(ByVal ORIGIN_SHEET_NAME As String, ByRef hairetsu() As String, ByVal rngTaishoColumn As Integer)

Dim myDic As Object, myKey As Variant Dim c As Variant, vardata As Variant Dim lp As Integer Dim Maxrow As Long Set myDic = CreateObject("Scripting.Dictionary") With Worksheets(ORIGIN_SHEET_NAME) Maxrow = .Cells(Rows.Count, rngTaishoColumn).End(xlUp).Row vardata = .Range(.Cells(2, rngTaishoColumn), .Cells(Maxrow, rngTaishoColumn)).Value End With For Each c In vardata If Not c = Empty Then If Not myDic.Exists(c) Then myDic.Add c, Null End If End If Next lp = 0 myKey = myDic.Keys ReDim hairetsu(lp) For i = 0 To myDic.Count - 1 If myKey(i) <> "*" Then hairetsu(lp) = myKey(i) lp = lp + 1 If i <> myDic.Count - 1 Then ReDim Preserve hairetsu(lp) End If End If Next i Set myDic = Nothing

End Sub

試したこと

補足情報(FW/ツールのバージョンなど)

MS Excel Office365 32Bit

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

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

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

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

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

mattuwan

2020/03/02 03:46

>3.フィルター後、2列目に現在表示されている値だけを別の配列に格納 この後はどうしたいのですか?
kuta

2020/03/02 07:05

>sinzou様 ご回答ありがとうございます。 確認しましたところ、少し応用は必要ですが実装できそうかなと思っています。 もう少し調べてみます。 >mattuwan様 ご回答ありがとうございます。 申し訳ありません。質問ではわかりやすいように2列で説明したのですが、 本番ではフィルタをかける列が4列あり、 また最終的に欲しい数値が記載された列が1列あります。 フィルタをかけたあと、その列の合計値をまとめシートに記載していく、という流れになります。 >3.フィルター後、2列目に現在表示されている値だけを別の配列に格納 →このあとは、2列目の配列の1番目の値でフィルタをかける  →フィルター後、3列目に現在表示されている値だけを別の配列に格納   →3列目の配列の1番目の値でフィルタをかける    →フィルター後、4列目に現在表示されている値だけを別の配列に格納   →4列目の配列の1番目の値でフィルタをかける    →別の列に記載されている値を合計してまとめ表に記載する   →4列目の配列の2番目の値でフィルタをかける・・・ という風に進める予定です。
meg_

2020/03/02 10:51

コードは「コードの挿入」で記入してください。
kuta

2020/03/03 08:42

meg_様 失礼しました。次回よりそのように記入致します。
guest

回答1

0

ベストアンサー

まず、VBE (VBAのエディタ) の上部メニューの ツール → 参照設定 を選択し、
「Microsoft Scripting Runtime」にチェックを入れてください。

その上で、質問者様が実施されたいことを実装しました。
質問から、A列~D列の文字列ごとにE列などに記載された数値の合計を算出したいのだと理解したので、
質問者様のプログラムとは大きく変更しましたが、
以下はそれなりにきれいな実装なのではないでしょうか。

VBA

1Option Explicit 2 3' 処理実行メインモジュールです 4Sub Main() 5 6 ' 変数宣言 7 Dim datas As Variant 8 Dim sheet As Worksheet 9 Dim filterdData() As Variant 10 Dim columnItemsA As Collection 11 Dim columnItemA As Variant 12 Dim columnItemsB As Collection 13 Dim columnItemB As Variant 14 Dim columnItemsC As Collection 15 Dim columnItemC As Variant 16 Dim columnItemsD As Collection 17 Dim columnItemD As Variant 18 19 ' 処理を実行するシートを取得します 20 Set sheet = ThisWorkbook.Worksheets("処理対象シート") 21 22 ' 全セルの値を取得します (但し1行目は空行のため2行目から取得します) 23 datas = readData(sheet, "A2") 24 25 ' 重複を排除したA列~D列の値を取得 26 Set columnItemsA = removeDuplication(getColumnData(datas, 1)) 27 Set columnItemsB = removeDuplication(getColumnData(datas, 2)) 28 Set columnItemsC = removeDuplication(getColumnData(datas, 3)) 29 Set columnItemsD = removeDuplication(getColumnData(datas, 4)) 30 31 ' 全ての値のパターンごとに行を抽出して合計値 (便宜的に5行目に数字が記載されていると仮定) を計算します 32 For Each columnItemA In columnItemsA 33 For Each columnItemB In columnItemsB 34 For Each columnItemC In columnItemsC 35 For Each columnItemD In columnItemsD 36 filterdData = filterData(datas, columnItemA, columnItemB, columnItemC, columnItemD) 37 Debug.Print (columnItemA & ", " & columnItemB & ", " & columnItemC & ", " & columnItemD & " = " & sumData(filterdData, 5)) 38 Next 39 Next 40 Next 41 Next 42 43End Sub 44 45' 指定列の数字を全て足した結果を返却します 46' 数字以外が混じっていた場合はVBAのエラーが発生します 47Private Function sumData(filterdData() As Variant, columnNumber As Long) As Long 48 49 ' 変数宣言 50 Dim i As Long 51 Dim sum As Long 52 53 ' 全ての数字を足します 54 sum = 0 55 If IsEmptyArray(filterdData) Then 56 sumData = sum 57 Exit Function 58 End If 59 For i = LBound(filterdData) To UBound(filterdData) 60 sum = sum + CLng(filterdData(i, columnNumber)) 61 Next 62 sumData = sum 63 64End Function 65 66' ワークシートのデータをフィルターした結果を返却します。 67' フィルター結果が存在はなかった場合は 空の配列 を返却します 68' 第1引数 : ワークシートのデータ 69' 第3引数 : 列ごとのフィルター定義リスト (フィルター不要な列については * を指定すること) 70Private Function filterData(ByRef datas As Variant, ParamArray filterWords() As Variant) As Variant() 71 72 ' 変数宣言 73 Dim i As Long 74 Dim j As Long 75 Dim k As Variant 76 Dim filterWordIndex As Long 77 Dim filterWordMaxIndex As Long 78 Dim numOfcolumn As Long 79 Dim result() As Variant 80 Dim isValidData As Boolean 81 Dim isMatchedRowNumbers As Collection 82 83 ' オブジェクトを初期化 84 Set isMatchedRowNumbers = New Collection 85 86 ' データの行数を取得します 87 numOfcolumn = UBound(datas, 2) - LBound(datas, 2) + 1 88 89 ' フィルター定義リストの最大indexを取得します 90 filterWordMaxIndex = UBound(filterWords) 91 92 ' 全行走査 93 For i = LBound(datas, 1) To UBound(datas, 1) 94 95 ' 処理対象行 96 isValidData = True 97 filterWordIndex = -1 98 For j = LBound(datas, 2) To UBound(datas, 2) 99 filterWordIndex = filterWordIndex + 1 100 If filterWordMaxIndex >= filterWordIndex Then 101 If filterWords(filterWordIndex) <> "*" And CStr(datas(i, j)) <> filterWords(filterWordIndex) Then 102 isValidData = False 103 Exit For 104 End If 105 End If 106 Next 107 108 ' フィルター条件に合致する行を記憶します 109 If isValidData Then 110 isMatchedRowNumbers.Add i 111 End If 112 113 Next 114 115 ' 合致する結果がない場合は Empty を返却します 116 If isMatchedRowNumbers.Count = 0 Then 117 ReDim result(0, 0) 118 filterData = result 119 Exit Function 120 End If 121 122 ' 結果を配列に設定します 123 i = 0 124 ReDim result(1 To isMatchedRowNumbers.Count, LBound(datas, 2) To UBound(datas, 2)) 125 For Each k In isMatchedRowNumbers 126 i = i + 1 127 For j = LBound(datas, 2) To UBound(datas, 2) 128 result(i, j) = datas(k, j) 129 Next 130 Next 131 132 ' 結果返却 133 filterData = result 134 135End Function 136 137' 二次元配列から特定の列のデータのみ取得します 138' 第2引数は、例えばA列の場合は 1 を指定します 139Private Function getColumnData(ByRef data As Variant, ByRef columnNumber As Long) As Collection 140 141 ' 変数宣言 142 Dim i As Long 143 Dim result As Collection 144 145 ' 結果初期化 146 Set result = New Collection 147 148 ' 全行走査して指定された列のデータを Collection に設定します 149 For i = LBound(data, 1) To UBound(data, 1) 150 result.Add data(i, columnNumber) 151 Next 152 153 ' 結果返却 154 Set getColumnData = result 155 156End Function 157 158' 処理を実施するワークシートの文字を読み取ります 159Private Function readData(ByRef targetSheet As Worksheet, Optional ByRef startFrom As String = "A1") As Variant 160 161 ' 使用済みの全てのセルの値を取得します 162 With targetSheet 163 readData = .Range(.Range(startFrom), getLastCell(targetSheet)) 164 End With 165 166End Function 167 168' 配列が空であるかを判定します 169Private Function IsEmptyArray(data() As Variant) As Boolean 170 171On Error GoTo ERROR 172 173 ' 配列のサイズを確認します 174 If (0 < UBound(data, 1)) Then 175 IsEmptyArray = False 176 Else 177 IsEmptyArray = True 178 End If 179 Exit Function 180 181ERROR: 182 183 'エラーが発生した場合は空の配列とみなします 184 IsEmptyArray = True 185 186End Function 187 188' シートで使用されている最も右下のセルを取得します 189Private Function getLastCell(ByRef sheet As Worksheet) As Range 190 With sheet.UsedRange 191 Set getLastCell = .Cells(.Cells.Count) 192 End With 193End Function 194 195' Collection型から重複を排除します 196Private Function removeDuplication(ByRef items As Collection) As Collection 197 198 ' 変数宣言 199 Dim dic As Scripting.Dictionary 200 Dim result As Collection 201 Dim item As Variant 202 203 ' オブジェクトを初期化します 204 Set dic = New Scripting.Dictionary 205 Set result = New Collection 206 207 ' Collectionから重複を排除します 208 For Each item In items 209 If Not dic.Exists(CStr(item)) Then 210 dic.Add CStr(item), "" 211 result.Add CStr(item) 212 End If 213 Next 214 215 ' 結果返却 216 Set removeDuplication = result 217 218End Function 219 220' Collectionの中身を標準出力します (デバック用の関数です) 221Private Sub showCollectionItems(ByRef items As Collection, Optional ByRef title As String = "") 222 Dim item As Variant 223 If title <> "" Then 224 Debug.Print title 225 End If 226 For Each item In items 227 Debug.Print CStr(item) 228 Next 229End Sub 230

投稿2020/03/02 11:31

編集2020/03/02 11:32
yamashita_yuich

総合スコア316

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

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

kuta

2020/03/03 08:43

baseballyama様 ご回答ありがとうございます。 頂いた内容と、使用したい様に少し改修しまして、何とか実装できそうです(^^) コードまで頂けるとは、大変助かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問