[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『連想配列を使用した「重複データを削除し合計を合算&平均値」』(みお)
VBA初心者です。連想配列について調べましたが、どうしても理想どおりに動かせません。
以下のコードで記号の重複を削除し、個数を合計することはできます。
が、1.平均を出すこと 2.複数列を移すこと
の2つができません。
図で示すとこうゆう形にしたいです。
A列 B列 C列 D列 E列 F列
10/1 α みかん 500 2 1500
10/1 β なし 900 5 2000
10/1 α みかん 450 3 1500
10/1 π りんご 200 4 1000
10/1 π りんご 300 2 1000
10/1 α みかん 550 6 1500
↓
H列 I列 J列 K列 L列 M列
10/1 α みかん 500 11 1500
10/1 β なし 900 5 2000
10/1 π りんご 250 6 1000
条件としては、
・基準はB列が重複しているかどうかのみ。他の列はD列とE列以外は上記のように同じ。
・D列はB列の重複しているものを加算して「合計数値÷加算数」で平均値をだしたい。
・E列はB列の重複しているものを加算する。
これらの条件で連想配列でVBAを組むことは可能でしょうか?
かなり試行錯誤しましたが、連想配列で組もうとするとうまくいきません。
また組めるならばコードを教えていただきたいです。
どうかよろしくおねがいします。
Sub 重複データを削除し合計を合算()
Dim myDic As Object
Dim myKey As Variant
Dim myItem As Variant
Dim myList As Variant
Dim i As Long
Set myDic = CreateObject("Scripting.Dictionary")
'A列〜F列のデータを配列に格納 myList = Range("A2", Range("A" & Rows.Count). _ End(xlUp)).Resize(, 6).Value
'連想配列にデータを格納 For i = 1 To UBound(myList, 1)
'記号が空欄かチェック If Not myList(i, 2) = Empty Then If Not myDic.exists(myList(i, 2)) Then
'重複しない記号を取得 myDic.Add Key:=myList(i, 2), Item:=myList(i, 5) Else
'個数を加算 myDic(myList(i, 2)) = myDic(myList(i, 2)) + myList(i, 5) End If End If Next
'[記号] 重複していないリストを格納 myKey = myDic.keys
'[個数] 個数の合計を格納 myItem = myDic.items
'リストを出力 For i = 0 To UBound(myKey) Cells(i + 2, 8).Value = myKey(i) Cells(i + 2, 9).Value = myItem(i) Next
'開放 Set myDic = Nothing
End Sub
< 使用 Excel:Excel2019、使用 OS:Windows10 >
A列、C列は必ずB列に連動しているか?などいろいろと気になるところはありますが、取り合えず平均を出すためには個数の情報が必要です。
Dictionary で別途集計することもできますが、関数を利用すると楽ではないでしょうか。 (ユニークな項目にするには AdvancedFilter なども利用できます。) とりあえずたたき台として、例示の結果となるサンプルです。
Sub Macro1() Dim myDic As New Scripting.Dictionary '' ツールの参照設定で Microsoft Scripting Runtime にチェック
Dim ky Dim r For r = 2 To Cells(Rows.Count, "B").End(xlUp).Row ky = Cells(r, "B") If ky <> "" Then If myDic.Exists(ky) = False Then myDic.Add ky, True End If Next
Range("I2").Resize(myDic.Count, 1) = WorksheetFunction.Transpose(myDic.Keys)
Dim numData numData = Cells(Rows.Count, "I").End(xlUp).Row - 1
Range("H2").Resize(numData, 1).Formula = "=XLOOKUP(I2,B:B,A:A,"""",-1)" Range("J2").Resize(numData, 1).Formula = "=XLOOKUP(I2,B:B,C:C,"""",-1)" Range("K2").Resize(numData, 1).Formula = "=AVERAGEIF(B:B,I2,D:D)" Range("L2").Resize(numData, 1).Formula = "=SUMIF(B:B,I2,E:E)" Range("M2").Resize(numData, 1).Formula = "=SUMIF(B:B,I2,F:F)"
With Range("H2").Resize(numData, 6) .Value = .Value End With Range("H2").Resize(numData, 1).NumberFormatLocal = Range("A2").NumberFormatLocal End Sub
(QS) 2021/10/05(火) 02:57
やりたいことは、ピボットテーブルで一発ですね
VBAの学習のためにDictionaryをつかってみたいということなら それはそれでいいので、その方向で回答しますが、 結果を得るための最適解はピボットテーブルだと思います。
VBAの勉強用としてまず回答しますけど、 Dictionaryはキーとアイテムが一対一の対応なので、 アイテムとして複数の値を格納できるものを用意しないといけないです。 (´・ω・`) 2021/10/05(火) 09:18
前のスレッドでも書きましたが、私は、やはりピボットテーブルの利用を推奨します。 "合計"も"平均"も"データの個数"も、簡単に計算ができます。 一度作ってしまえば、データ更新処理(セル範囲を指示するだけです)し、 それすらマクロで簡単にできます。マクロなしで、自動指定する方法もあります。 ごりごりコードを書くのは、できるだけ避けたほうが無難です。
どうしてもということなら、以下のようにできます。 あえて、より簡単な例で説明させていただきます。 ご自分の例に応用してみてください。
売上高を データの個数で割った平均(意味があるかどうか知りませんが)を 計算する例です。
A列 B列 C D E F 1 品名 売上高 品名 平均 2 a 10 a 25 3 b 20 b 35 4 c 30 c 45 5 a 40 6 b 50 7 c 60
上記のA列、B列のデータをもとに、計算結果をE,F列に書き出しています。 参考にしてください。
Sub test() Dim k As Long Dim dic As Object Dim a() ' a は arrayの頭1文字の積もり Dim key Dim s As String Dim v As Double
Set dic = CreateObject("Scripting.Dictionary") For k = 2 To Cells(Rows.Count, "A").End(xlUp).Row s = Cells(k, "A") v = Cells(k, "B") If Not dic.exists(s) Then ReDim a(1 To 2) a(1) = v a(2) = 1 Else a = dic(s) a(1) = a(1) + v a(2) = a(2) + 1 End If dic(s) = a Next k = 1 For Each key In dic k = k + 1 Cells(k, "E") = key Cells(k, "F") = dic(key)(1) / dic(key)(2) Next End Sub
なお、 dic(s)(1) = dic(s)(1) + v dic(s)(2) = dic(s)(2) + 1 などと書ければよいのですが、それはうまくいきません。
(γ) 2021/10/05(火) 09:50
連想配列の勉強の為ということなら、ご参考までに。
元のコードをなるべく活かすという方針でコーディングしてます。
γさんのコード例のように Item に配列を格納するという方法です。
配列は1行分のデータをすべて格納します。
平均を出すには、件数が必要ですが、配列の要素数を一つ増やしてそこに件数を格納します。
その件数を利用して平均をその都度、計算してます。
出力に関しては、
Items配列はジャグ配列(配列の配列)になりますので、Transposeを2回かけることで
二次元配列に変換して、セル範囲に直接出力するようにしてみました。
配列の最終列の件数は不要なので出力するときに列数を一つ減らしてます。
Sub 重複データを削除し合計を合算() Dim myDic As Object Dim myItem As Variant Dim myList As Variant Dim i As Long Set myDic = CreateObject("Scripting.Dictionary")
'A列〜F列のデータを配列に格納 myList = Range("A2", Range("A" & Rows.Count). _ End(xlUp)).Resize(, 6).Value '連想配列にデータを格納 For i = 1 To UBound(myList, 1) Dim a '記号が空欄かチェック If Not myList(i, 2) = Empty Then If Not myDic.Exists(myList(i, 2)) Then a = Array(myList(i, 1), myList(i, 2), myList(i, 3), _ myList(i, 4), myList(i, 5), myList(i, 6), 1)
myDic.Add Key:=myList(i, 2), _ Item:=a Else a = myDic(myList(i, 2)) a(3) = (a(3) * a(6) + myList(i, 4)) / (a(6) + 1) 'D列平均 a(6) = a(6) + 1 '件数 a(4) = a(4) + myList(i, 5) 'E列計 myDic(myList(i, 2)) = a End If End If Next
myItem = myDic.Items 'ジャグ配列→二次元配列変換 myItem = WorksheetFunction.Transpose(WorksheetFunction.Transpose(myDic.Items))
'リストを出力 Cells(2, 8).Resize(UBound(myItem), UBound(myItem, 2) - 1).Value = myItem '開放 Set myDic = Nothing End Sub
(hatena) 2021/10/05(火) 14:32
こんばんは! 皆さんの回答を参考にしながら自分流に書くとどうなるのかなと思って久しぶりに書いてみました。 結果は、こんな感じになりました。。。。 何かの参考になれば幸いです。。。。 おやすみなさいzzzzzzzzzzzzzzzzzzzzzzzzzz
Option Explicit Sub 重複データを削除し合計を合算() Dim myDic As Object Dim myKey As Variant Dim myItem As Variant Dim myList As Variant Dim x As Variant Dim y As Variant Dim z As Variant Dim i As Long Set myDic = CreateObject("Scripting.Dictionary") 'A列〜F列のデータを配列に格納 myList = Range("A2", Range("A" & Rows.Count). _ End(xlUp)).Resize(, 6).Value '連想配列にデータを格納 For i = 1 To UBound(myList, 1) '記号が空欄かチェック If Not myList(i, 2) = Empty Then If Not myDic.Exists(myList(i, 2)) Then '重複しない記号を取得 ReDim y(0) y(0) = myList(i, 4) myDic(myList(i, 2)) = Array(Application.Index(myList, i, 0), y) Else x = myDic(myList(i, 2))(0) y = myDic(myList(i, 2))(1) ReDim Preserve y(UBound(y) + 1) y(UBound(y)) = myList(i, 4) x(4) = Application.Average(y) x(5) = x(5) + myList(i, 5) myDic(myList(i, 2)) = Array(x, y) End If End If Next myItem = myDic.Items ReDim z(UBound(myItem)) For i = LBound(myItem) To UBound(myItem) z(i) = myItem(i)(0) Next 'ジャグ配列→二次元配列変換 z = Application.Transpose(Application.Transpose(z)) 'リストを出力 Cells(2, 8).Resize(UBound(z, 1), UBound(z, 2)).Value = z '開放 Set myDic = Nothing Erase myItem, myList, x, y, z End Sub (SoulMan) 2021/10/06(水) 00:28
平均計算のため、件数列が必要なので、前のコードではArray関数で配列を生成してましたが、ちょっと冗長だなと感じていました。
データを配列に格納するとき、1列分余分に格納して、その列を件数列にすればいいのでは、と思いつきました。
Sub 重複データを削除し合計を合算() Dim myDic As Object Dim myItem As Variant Dim myList As Variant Dim i As Long Set myDic = CreateObject("Scripting.Dictionary") 'A列〜F列のデータに、件数格納用にG列を加えた範囲を配列に格納 myList = Range("A2", Range("A" & Rows.Count). _ End(xlUp)).Resize(, 7).Value '連想配列にデータを格納 For i = 1 To UBound(myList, 1) Dim a '記号が空欄かチェック If Not myList(i, 2) = Empty Then If Not myDic.Exists(myList(i, 2)) Then a = Application.Index(myList, i, 0) a(7) = 1 myDic.Add Key:=myList(i, 2), Item:=a Else a = myDic(myList(i, 2)) a(4) = (a(4) * a(7) + myList(i, 4)) / (a(7) + 1) 'D列平均 a(7) = a(7) + 1 '件数 a(5) = a(5) + myList(i, 5) 'E列計 myDic(myList(i, 2)) = a End If End If Next 'Itemsのジャグ配列→二次元配列変換 myItem = Application.Transpose(Application.Transpose(myDic.Items))
'リストを出力 Cells(2, 8).Resize(UBound(myItem), UBound(myItem, 2) - 1).Value = myItem '開放 Set myDic = Nothing End Sub
ちょっとだけコードがスッキリしました。
(hatena) 2021/10/06(水) 09:18
じゃ、私も参加。 後出しなのでちょっと方法を変えて
----- クラスモジュール Class1 ------- Public Count As Long Public Sum As Double Public Property Get Average() As Variant If Count <> 0 Then Average = Sum / CDbl(Count) Else Average = CVErr(lErrDiv0) End If End Property ----- 標準モジュール ------- Sub sample() Dim i As Long Dim dic As Object Dim key As Variant Dim buf() As Variant Set dic = CreateObject("Scripting.Dictionary") For i = 1 To Cells(Rows.Count, "C").End(xlUp).Row key = Cells(i, "C").Value If Not dic.Exists(key) Then dic.Add key, New Class1 End If With dic(key) .Count = .Count + 1 .Sum = .Sum + Cells(i, "D") End With Next ReDim buf(1 To dic.Count, 1 To 2) i = 1 For Each key In dic.keys buf(i, 1) = key buf(i, 2) = dic(key).Average i = i + 1 Next Cells(1, "J").Resize(dic.Count, 2).Value = buf End Sub (´・ω・`) 2021/10/06(水) 09:20
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.