連想配列2-Dictionaryオブジェクトの使用例

この講座は有料講座です。
講座の購入後ご覧になれます。

ログインアカウントの新規作成

解説

Dictionaryオブジェクトについて、使用例を用いて解説しています。

この教材についての過去の質問・感想

12613 : 小川慶一の回答 (2020-11-15 09:48:22)

たかちゃんさん:

> 在宅ワークなどで、家のパソコンのOSがWindowsだったりMacだったりするとややこしそうです。。。

一般論としては、環境が統一されていたほうが仕事ははかどりますね。


12602 : たかちゃんさんのコメント (2020-11-13 04:27:41)

田中さん、小川先生

今回勉強した重複チェックパターンはしっかりメモっておきました。
Dictionaryは便利ですが、出来上がったマクロを動かすだけの人のパソコンにも設定しないといけない点が不便だなぁと思いました。
在宅ワークなどで、家のパソコンのOSがWindowsだったりMacだったりするとややこしそうです。。。
> たかちゃんさん:
>
> 私の経験上、実務で大量データをマクロで処理するとき、重複チェックは頻出です。Dictionaryを使えると作業効率が格段に向上しますね。


12600 : 小川慶一の回答 (2020-11-12 11:37:05)

田中 宏明さん:

dictionaryは、vbaで使う場合手続きが多いのが(他言語に比して)やや残念ですね。

lookupの方法はいろいろあります。
それらをまとめた講座を作りたいなと思っています。(なかなか手を付けられていませんが)


12597 : 田中 宏明さんのコメント (2020-11-11 22:43:41)

たかちゃんさん:

私の経験上、実務で大量データをマクロで処理するとき、重複チェックは頻出です。Dictionaryを使えると作業効率が格段に向上しますね。

> 田中さん:
> 色々ありがとうございます!先程、もう少し込み入ったプログラムを書きましたが、ちゃんと動きました。便利ですし簡単にかけるので最高ですね。


12596 : たかちゃんさんのコメント (2020-11-11 21:17:15)

田中さん:
色々ありがとうございます!先程、もう少し込み入ったプログラムを書きましたが、ちゃんと動きました。便利ですし簡単にかけるので最高ですね。Dictionary.clsの中身を見た時、何も理解できそうになく・・もうダメだと思いました。が、田中さんに教えてもらったあの2行で、驚くほど簡単に解決しました。本当に試して良かったです。ありがとうございます!

> たかちゃんさん:
>
> MacでDictionaryの学習をする場合、発展編1で学習したステップオーバーがお薦めです。ステップイン([F8])は、cls内の処理を通り、大変なので…
>
> ・ステップオーバー([Shift]+[F8])
> 呼び出し元にある、呼び出し先のマクロ名にカーソルがある時にステップオーバーを実行すると、その呼び出し先のマクロを実行した後、呼び出し元のマクロに戻ります。


12594 : 田中 宏明さんのコメント (2020-11-11 19:47:25)

たかちゃんさん:

MacでDictionaryの学習をする場合、発展編1で学習したステップオーバーがお薦めです。ステップイン([F8])は、cls内の処理を通り、大変なので…

・ステップオーバー([Shift]+[F8])
呼び出し元にある、呼び出し先のマクロ名にカーソルがある時にステップオーバーを実行すると、その呼び出し先のマクロを実行した後、呼び出し元のマクロに戻ります。

> Dicの所を通過する度に、Dictionary.cls内の処理を行うような感じで、動きました。とりあえず、これでDictionaryの勉強を再開してみようと思います。


12590 : 田中 宏明さんのコメント (2020-11-10 21:53:59)

たかちゃんさん:

良かったです。
努力すれば道が開けるとはこのことですね。

> 田中さん、小川先生
> 以下、田中さんから教えて頂いた方法で、簡単なDictionaryを使ったプログラムを動かした所、動きました!


12589 : たかちゃんさんのコメント (2020-11-10 21:49:05)

田中さん、小川先生
以下、田中さんから教えて頂いた方法で、簡単なDictionaryを使ったプログラムを動かした所、動きました!
【手順】
1.Dictionary.clsをモジュールのフォルダから右クリックでインポート。
(するとクラスモジュールというフォルダが作られてその中にDictionary.clsが入ります。)
2.その状態で、Dictionaryのプログラムを書いて、宣言の所に教えて頂いた通りに変更。
Dicの所を通過する度に、Dictionary.cls内の処理を行うような感じで、動きました。とりあえず、これでDictionaryの勉強を再開してみようと思います。
ありがとうございました。(^^)

Sub test()
    Dim dic As Dictionary
    Set dic = New Dictionary
    dic.Add "小川", 83
    dic.Add "山田", 75
    dic.Add "田中", 92
    Debug.Print dic.Count
    
    Debug.Print dic.Item("小川")
    
    If Not dic.Exists("佐藤") Then
        dic.Add "佐藤", 50
    End If
    
    Debug.Print dic.Count
End Sub 


> たかちゃんさん:
>
> その後、Mac版でDictionaryを使う方法を調べてみました。以下から Source code (zip) をダウンロードhttps://github.com/VBA-tools/VBA-Dictionary/releases/tag/v1.4.1
>
> 解凍された複数あるファイルのうち Dictionary.cls をVBEからインポートすると動きました。
> (Dictionary.clsの条件付コンパイルを記述を変更し、Windows版エクセルで強制的にMac動作)
>
> 標準モジュールの変数宣言を以下のように変更します。
>
Dim dic As Dictionary 
> Set dic = New Dictionary
>  

>
> お時間と興味があれば試してみてください。


12581 : 小川慶一の回答 (2020-11-07 05:00:11)

たかちゃんさん、田中さん:

おはようございます。
お二人とも素晴らしいです。僕の出る幕はもはやありません... (^^;


12577 : たかちゃんさんのコメント (2020-11-05 05:02:04)

田中さん:

> 今日、会社の仕事で「重複のないリスト作成」の機会があり、さっそくAdvancedFilterを使ってみたところ、見事に失敗しました。

私も失敗して1日悩みました。。(^^;
以下の動画、今日初めてチェックしました。有益な情報ありがとうございます。この機能も初めて知りました。

> そこで、たまたま昨日復習した別の動画【10秒でできる!エクセルデータの「重複の削除」】のやり方で、速攻解決しました。
> https://online.pc5bai.com/movie/index/72/1280
>
> 達人養成塾に入り、ここでやりとりさせていただき、本当に良かったです。
>


12576 : 田中 宏明さんのコメント (2020-11-04 21:39:08)

たかちゃんさん:

今日、会社の仕事で「重複のないリスト作成」の機会があり、さっそくAdvancedFilterを使ってみたところ、見事に失敗しました。

そこで、たまたま昨日復習した別の動画【10秒でできる!エクセルデータの「重複の削除」】のやり方で、速攻解決しました。
https://online.pc5bai.com/movie/index/72/1280

達人養成塾に入り、ここでやりとりさせていただき、本当に良かったです。

> ご存知かもしレませんが、AdvancedFilter使用時はタイトル行から指定しないと、エラーになりませんが変な動きをしますのでお気をつけ下さい。


12574 : たかちゃんさんのコメント (2020-11-04 01:18:43)

田中さん:
色々検証して頂いてありがとうございました。
声かけて貰えなかったら、多分ここまで頑張れませんでした。
ご存知かもしレませんが、AdvancedFilter使用時はタイトル行から指定しないと、エラーになりませんが変な動きをしますのでお気をつけ下さい。今回の例はタイトルがなかったので気付きませんでした。コメント12563の小川先生が書かれたコードがとても参考になりました。
> たかちゃんさん:
>
> 拝見しました。
> 基本に忠実なコードですね。実行しなくても検証されたこと、関数のエラー対策を理解できました。


12572 : 田中 宏明さんのコメント (2020-11-03 23:00:28)

たかちゃんさん:

拝見しました。
基本に忠実なコードですね。実行しなくても検証されたこと、関数のエラー対策を理解できました。

> 小川先生、田中さん
> 色々とありがとうございます。
> 私の方でも検証し、Collectionでは重複しないリストを作るところまではできましたので記念に投稿


12571 : たかちゃんさんのコメント (2020-11-03 22:23:00)

小川先生、田中さん
色々とありがとうございます。
私の方でも検証し、Collectionでは重複しないリストを作るところまではできましたので記念に投稿します。(動作確認済み)
カウントをしたい場合は、やはり先日教えて頂いたCountifなど別の方法でする必要があります。
Function部分のnamaeやsの変数は、For eachを使用する関係でStringではなくVariantになっています。一応動きますが、邪道な感じがしますね・・・。


■Chap02-71のSheet1課題
 C列に重複のない名前リストをCollection使用で作成した場合

Sub Sample()
    Dim dic As Collection
    Set dic = New Collection
    Dim c As Long
    Dim st As String
 
    For c = 1 To Range("A" & Rows.Count).End(xlUp).Row
        st = Range("A" & c).Value
        If c > 1 Then
            If isExists(dic, st) = False Then
                dic.Add st
            End If
        Else
            dic.Add st
        End If
    Next
    
    Debug.Print dic.Count
    
    For c = 1 To dic.Count
    Range("C" & c).Value = dic.item(c)
    Next
End Sub

Function isExists(dic As Collection, namae As Variant) As Boolean
    Dim s As Variant
    Dim hantei As Boolean
    hantei = False
    
    For Each s In dic
        If s = namae Then
            hantei = True
        End If
    Next
    isExists = hantei
End Function 



> たかちゃんさん:
>
> Mac版のエクセルで、ScriptingDictionaryが使えないとのことで、Collectionオブジェクトでの代用を検証してみました。
> 重複のないリスト作成はできそうですが、CountやSumのような集計は無理だとわかりました。(Windowsで動作検証済みです。)


12570 : 田中 宏明さんのコメント (2020-11-03 19:10:09)

たかちゃんさん:

その後、Mac版でDictionaryを使う方法を調べてみました。以下から Source code (zip) をダウンロードhttps://github.com/VBA-tools/VBA-Dictionary/releases/tag/v1.4.1

解凍された複数あるファイルのうち Dictionary.cls をVBEからインポートすると動きました。
(Dictionary.clsの条件付コンパイルを記述を変更し、Windows版エクセルで強制的にMac動作)

標準モジュールの変数宣言を以下のように変更します。

Dim dic As Dictionary 
Set dic = New Dictionary
 


お時間と興味があれば試してみてください。


12569 : 田中 宏明さんのコメント (2020-11-03 12:09:26)

小川慶一さん:

助言ありがとうございます。やり方がわかりました。
Excel標準機能で重複のないリストが簡単にできるとは驚きです。

> 田中 宏明さん:
>
> 「フィルターオプションの設定」ダイアログで、「重複するレコードは無視する」にチェックを入れて元の表とは違うところを出力先として指定してオートフィルターします。


12568 : 田中 宏明さんのコメント (2020-11-03 11:18:34)

たかちゃんさん:

Mac版のエクセルで、ScriptingDictionaryが使えないとのことで、Collectionオブジェクトでの代用を検証してみました。
重複のないリスト作成はできそうですが、CountやSumのような集計は無理だとわかりました。(Windowsで動作検証済みです。)

 'Scripting.Dictionaryが使えない場合の代用検証
Sub DicSampleCollection()
    Dim dicC As Collection
    Set dicC = New Collection
    
    'Collectionへ要素の追加を行います
        'CollectionのAddメソッドはキーとアイテムが逆順
    dicC.Add 83, "小川"
    dicC.Add 75, "山田"
    dicC.Add 92, "田中"
    
    '要素数を数えます
    Debug.Print dicC.Count
    
    'キーから値を取り出します
    Debug.Print dicC.Item("小川")
    
    'キーの値に1を加算
    '【検証1】Collectionに入れたアイテムを変更できませんでした。
    '以下はエラー
    'dicC.Item("小川") = dicC.Item("小川") + 1

    '特定のキーが存在するか調べ、条件分岐します
    '【検証2】Existsは関数で代用可能
    If Not ExistsItem(dicC, "佐藤") Then
        'CollectionのAddメソッドはキーとアイテムが逆順
        dicC.Add 50, "佐藤"
    End If
    
    Dim ColItem As Variant
    
    'キーのリストを取得
    '【検証3】Collectionのキーを取得できませんでした。

    '値のリストを取得します
    '【検証4】Collectionのアイテムを取得できました。
    For Each ColItem In dicC
        Debug.Print ColItem
    Next
End Sub

'http://blog.livedoor.jp/midorityo/archives/51067902.html
Function ExistsItem(ByRef obj As Variant, ByVal index As Variant) As Boolean
    Dim dammy As Variant, rtn As Boolean
    On Error Resume Next
    Err.Clear
    If IsObject(obj) Then
        dammy = IsObject(obj.Item(index))
    Else
        dammy = obj(index)
    End If
    If Err.Number = 0 Then
        rtn = True
    Else
        rtn = False
        If Err.Number = 438 Then MsgBox ("このコレクションにItemプロパティが存在しないため、判別できません")
    End If
    ExistsItem = rtn
End Function


12567 : 小川慶一の回答 (2020-11-03 10:45:35)

たかちゃんさん:

> 頂いたプログラムと見比べて、何でつまづいていたのか検証しようと思います。

公式サイトのドキュメントも参考にされると良いかと。

"worksheetfunction sumif vba" 等のキーワードで検索し、検索結果一覧から、 docs.microsoft.com のドメインのものを見つけます。
https://docs.microsoft.com/ja-jp/office/vba/api/excel.worksheetfunction.sumif

公式ドキュメントを読めるようになると、また格段に力がつきます。


12566 : たかちゃんさんのコメント (2020-11-03 10:05:49)

小川慶一さん:
先生、ありがとうございます。
今日はまさにこの動きのプログラムを書こうとして試行錯誤していました。
頂いたプログラムと見比べて、何でつまづいていたのか検証しようと思います。このパターンは事務仕事で良く使いそうです。

昔、良くエクセルの小計?、集計?機能で大量のデータを集計していた時の事を思い出しました。結構、説明しても使い方が良く分からないという方が多かったのですが、vbaできたらマクロ作ってボタンを設置すれば誰でも使える様になりますね。(^^)

> たかちゃんさん:
>
> >さらに何かをするとDictionaryが使える様になるという情報がインターネットにありましたが
>
> 何なんでしょうね。
>
> sumifもやるならたとえば以下の要領。
> worksheetfunctionは引数にセル範囲を指定するときに最初戸惑うかもしれません。が、そこを乗り越えれば、ピボットテーブル的なクロス集計表もそんなに苦労せずに作れるようになります。
>


12564 : 小川慶一の回答 (2020-11-03 05:17:33)

たかちゃんさん:

今紹介したサンプルは、元データが「伝票作成マクロ」のシート「main」のものの場合の例です。


12563 : 小川慶一の回答 (2020-11-03 05:15:13)

たかちゃんさん:

>さらに何かをするとDictionaryが使える様になるという情報がインターネットにありましたが

何なんでしょうね。

sumifもやるならたとえば以下の要領。
worksheetfunctionは引数にセル範囲を指定するときに最初戸惑うかもしれません。が、そこを乗り越えれば、ピボットテーブル的なクロス集計表もそんなに苦労せずに作れるようになります。

Sub count_and_sumif_all()
    Dim rgBase As Range
    Set rgBase = Range("B1:B" & Range("B" & Rows.Count).End(xlUp).Row)
    rgBase.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("K1"), Unique:=True
    
    Range("K1").Copy Destination:=Range("L1:M1")
    Range("L1").Value = "度数"
    Range("M1").Value = "合計"
     
    Dim rg As Range
    For Each rg In Range("K2:K" & Range("K" & Rows.Count).End(xlUp).Row)
        rg.Offset(, 1).Value = WorksheetFunction.CountIf(rgBase, rg.Value)
        rg.Offset(, 2).Value = WorksheetFunction.SumIf(rgBase, rg.Value, rgBase.Offset(, 5))
    Next
End Sub


12562 : 小川慶一の回答 (2020-11-03 04:53:10)

田中 宏明さん:

「フィルターオプションの設定」ダイアログで、「重複するレコードは無視する」にチェックを入れて元の表とは違うところを出力先として指定してオートフィルターします。
この操作を自動記録しつつ行うと advancedfilter を使ったコードを得られます。


12561 : たかちゃんさんのコメント (2020-11-02 22:42:49)

小川先生、田中さん

おはようございます。
毎回、ありがとうございます。
For eachとCountif 思いつきそうで、思いつきませんでした。(^^;
Advanced Filterは、エクセルの機能で普通にありますが、実は今回調べるまで知りませんでした。vbaと同時にエクセルの機能ももっと勉強しないとなぁと思いました。Mac版でも、恐らく誰かが作った?.clsという拡張子のファイルをクラスモジュールにインポーとして、さらに何かをするとDictionaryが使える様になるという情報がインターネットにありましたが、「さらに何かをする」の部分が全く理解できず断念しました。取り敢えず、工夫すれば他の方法でもなんとかできることが知れて安心しました。
他の受講生さんから声をかけて貰えると嬉しいです。ありがとうございます!(^^)




> たかちゃんさん:
> 私はDictionary大好き受講生です。
> AdvancedFilterを使えば、Dictionaryを使わなくても簡単に重複のないリストを作成できるのですね。
> とても参考になりました。
>


12559 : 田中 宏明さんのコメント (2020-11-02 21:36:44)

たかちゃんさん:
私はDictionary大好き受講生です。
AdvancedFilterを使えば、Dictionaryを使わなくても簡単に重複のないリストを作成できるのですね。
とても参考になりました。


> Mac版のエクセルで、ScriptingDictionaryが使えない為、何とか似た様な方法で解決できないか
> ずっと考えていました。Collectionオブジェクトという、Dictionaryと似た様なものも調べてみたのですが
> Existsメソッドもなく思った様には使えませんでした。(><)
> とりあえず、配列を使って書いてみましたので、記念に投稿します。(正常動作確認済み)


12557 : 小川慶一の回答 (2020-11-02 16:09:13)

たかちゃんさん:

こんにちは。

外部ライブラリ由来の機能を使う場合、OSが異なることのデメリットがでますね。

> Dictionaryが使えない場合、この様な動きのプログラムを書くにはどんな方法が良さそうでしょうか?

試されたとおり、エクセルに一時集計用シートを用意してそこに書き出すのがいちばん簡単かなと思います。
AdvancedFilterを使うのは良い発想だと思います。

Scripting.Dictionary の機能を紹介するために作ったサンプルです。
Scripting.Dictionary を使うという制約をはずし、「なんでも良いからとにかくシンプル高速に」ということであれば、以下ですかね。

Sub CountAll()
    Dim rgBase As Range
    Set rgBase = Range("A1:A" & Range("A" & Rows.Count).End(xlUp).Row)
    rgBase.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("F1"), Unique:=True
    
    Dim rg As Range
    For Each rg In Range("F1:F" & Range("F" & Rows.Count).End(xlUp).Row)
        rg.Offset(, 1).Value = WorksheetFunction.CountIf(rgBase, rg.Value)
    Next
End Sub


12554 : たかちゃんさんのコメント (2020-11-02 02:07:46)

Mac版のエクセルで、ScriptingDictionaryが使えない為、何とか似た様な方法で解決できないか
ずっと考えていました。Collectionオブジェクトという、Dictionaryと似た様なものも調べてみたのですが
Existsメソッドもなく思った様には使えませんでした。(><)
とりあえず、配列を使って書いてみましたので、記念に投稿します。(正常動作確認済み)
Dictionaryが使えない場合、この様な動きのプログラムを書くにはどんな方法が良さそうでしょうか?

他にも、基礎編の「細巻き?」で勉強したFor文を使った方法でも書いてみましたが、名前でソートをする手間が省けるので、配列使った方が少しは早いのかなぁと思いました。

■Chap02-71 Sheet1の課題■

Sub test1()
    Dim sList() As String
    Dim mx As Long
    Dim c As Long
    '重複しないリストをC1に書き出し
    Range("A1:A273").AdvancedFilter Action:=xlFilterCopy, _
    CopyToRange:=Range("C1"), _
    Unique:=True
    
    '上記リストを配列に格納
    mx = Range("C" & Rows.Count).End(xlUp).Row
    For c = 1 To mx
        ReDim Preserve sList(1, c)
        sList(0, c - 1) = Range("C" & c).Value
    Next
    
    Dim rg As Range
    Dim cnt As Long
    '重複回数をカウントし、配列に格納
    For c = LBound(sList, 2) To UBound(sList, 2)
            For Each rg In Range("A1:A273")
            If sList(0, c) = rg.Value Then
                cnt = cnt + 1
                sList(1, c) = cnt
            End If
        Next
        cnt = 0
    Next
    
    '結果をD1に書き出し
    For c = LBound(sList, 2) To UBound(sList, 2)
        Range("D1").Offset(c).Value = sList(1, c)
    Next
    
End Sub
 


10369 : 小川慶一の回答 (2018-08-10 10:59:54)

mokoさん:

ひきつづき、練習しつつ、実務でもガシガシマクロ書いてください。
それがいちばんです。



> >戻り値は、「Variant()」でしたよね。
> つまり、戻り値は、「Variant型」でなく、「Variant型の配列」です。こういうところ、しっかり詰めてください。
>
> 型を調べたときにVariantにカッコがついているのが不思議だったのですが、配列という意味だったのですね。
> 理解しました。ありがとうございます。
>
> >要は、「そういう言語の仕様だ」ということです。(*)
>
> redimについても疑問もすっきりしました(^^♪
> そっちのほうが都合がよいということですね。
>
> 配列はまだ全然慣れていなくて、どういうときに使うとよいのか?とさぐりさぐりテストしている段階です。
> 自信をもって使えるようになるまで、まだ時間がかかりそうですがおもしろがりつつ頑張ります!


10366 : mokoさんのコメント (2018-08-09 23:09:25)

>戻り値は、「Variant()」でしたよね。
つまり、戻り値は、「Variant型」でなく、「Variant型の配列」です。こういうところ、しっかり詰めてください。

型を調べたときにVariantにカッコがついているのが不思議だったのですが、配列という意味だったのですね。
理解しました。ありがとうございます。

>要は、「そういう言語の仕様だ」ということです。(*)

redimについても疑問もすっきりしました(^^♪
そっちのほうが都合がよいということですね。

配列はまだ全然慣れていなくて、どういうときに使うとよいのか?とさぐりさぐりテストしている段階です。
自信をもって使えるようになるまで、まだ時間がかかりそうですがおもしろがりつつ頑張ります!



10350 : 小川慶一の回答 (2018-08-09 12:59:18)

mokoさん:

> dic.Keysもdic.ItemsもTypeNameで型を調べてみたらどちらもVariantでした。

戻り値は、「Variant()」でしたよね。
つまり、戻り値は、「Variant型」でなく、「Variant型の配列」です。こういうところ、しっかり詰めてください。


☆ポイント:

[1]
DictionaryオブジェクトのKeysメソッド、Itemsメソッドの戻り値はVariant型の配列です。
たとえ内部データがInterger型、String型だったとしても、受取人たる左辺の変数はVariant型の配列でなければなりません。

[2]
配列でなく通常のVariant型で受取人を宣言しても動作します。
それは、Variant型は何にでも使える型なので(発展編1第1章参照)、Variant型の配列としても使えるからです。


> 疑問1と疑問2の動的配列で宣言してもエラーがでないまま動いたことについては未だに理解できていません。((+_+))

動的配列がRedimで事前にサイズ指定することなしにいきなり配列を受け取れるほうがプログラマーにとっては取扱いしやすいでしょう。
「ほうが」というのは、「配列を変数が受け取るときは、事前に受け取ろうとする配列のサイズを調べ、そのサイズに合うようにRedimしなくてはならない場合と比べて」という意味です。

という説明でどうでしょうか。
要は、「そういう言語の仕様だ」ということです。(*)

以下では、[1], [2]に注目してください。

Sub test()
    Dim dic As Scripting.Dictionary
    Set dic = CreateObject("Scripting.Dictionary")
     
    Dim c As Long
    Dim cMx As Long
    Dim st As String
    cMx = Range("A" & Rows.Count).End(xlUp).Row
     
    For c = 1 To cMx
        st = Range("A" & c).Value
        If dic.Exists(st) Then
            dic.Item(st) = dic.Item(st) + 1
        Else
            dic.Add (st), 1
        End If
    Next
     
    '★疑問1 vKeysとvItmsを動的配列で宣言しても同じ結果になった
    '★疑問2 配列のサイズがわからなかったのでredimしていないがエラーが出なかった
    '★疑問3 vKeysの型をstring,vItmsの型をlongにしたら『型が一致しません』エラーになる
    Dim vKeys() As Variant
    Dim vItms() As Variant
    
    Debug.Print TypeName(dic.Keys)  '[1]
    Debug.Print TypeName(dic.Items) '[2]
    vKeys() = dic.Keys
    vItms() = dic.Items
     
    For c = LBound(vKeys) To UBound(vKeys)
        Range("C1").Offset(c).Value = vKeys(c)
        Range("D1").Offset(c).Value = vItms(c)
    Next
End Sub


(*) 似たようなクレーム(?)でよくあるのが、以下のような関数の挙動についてのものです。
基礎編で対面講座をやると2割くらいの方が疑問に思うのは、「なぜ、Instr関数の戻り値は、常に、調査対象の文字が『左から数えて○文字目』という情報なのだろう。6文字目からさがして9文字目で見つかったのだから、こういう関数は、戻り値は『9』ではなく『3』であるべきではないか?」とか、そういうものです。

Sub InstrSample()
    Dim c As Long
    c = InStr(6, "神奈川県/横浜市/港北区", "/")
    Debug.Print c
End Sub


でも、Instr関数の戻り値が常に「左から数えて」であってくれたほうが、プログラマーにとっては取扱いが簡単です。
mokoさんも、ある程度経験を積まれたあとなのでそのことは今となってはすぐにピンと来るかと思います。

ニュアンスはそれと同じです。
要は、「そういう言語の仕様だ」ということです。よりはっきり言ってしまえば、「この仕様がしっくりこないのは、理解力ではなく、演習と経験の量の問題だ」ということです。
僕としては、「配列を使った実装をある程度経験して、それでもまだ同じことを疑問に思うようならまた聞いてください。Instr関数の場合と同様、たぶん、ある程度実装経験を積んだら、この質問自体バカバカしいと感じられるようになるでしょう」とお返事したいところ。


> 小川先生
> ヒントのコード、ありがとうございます。
>
> 変数にも配列を入れられるとは思っていなかったので、とても驚きました。
> 配列は静的配列か動的配列かで宣言するものだと思いこんでました。
> 今回のようにdic.Keysやdic.Itemsで作った配列をそのままいれるのであれば、配列よりも変数で宣言したほうがシンプルでわかりやすいと思いました。
> ・・・が、疑問1と疑問2の動的配列で宣言してもエラーがでないまま動いたことについては未だに理解できていません。((+_+))
>
> 疑問3は理解しました。
> dic.Keysもdic.ItemsもTypeNameで型を調べてみたらどちらもVariantでした。だから他の型で宣言するとエラーがでたんですね。
> 型が違うというエラーがでたらTypeNameで調べて修正していきます。


10332 : mokoさんのコメント (2018-08-07 00:03:07)

小川先生
ヒントのコード、ありがとうございます。

変数にも配列を入れられるとは思っていなかったので、とても驚きました。
配列は静的配列か動的配列かで宣言するものだと思いこんでました。
今回のようにdic.Keysやdic.Itemsで作った配列をそのままいれるのであれば、配列よりも変数で宣言したほうがシンプルでわかりやすいと思いました。
・・・が、疑問1と疑問2の動的配列で宣言してもエラーがでないまま動いたことについては未だに理解できていません。((+_+))

疑問3は理解しました。
dic.Keysもdic.ItemsもTypeNameで型を調べてみたらどちらもVariantでした。だから他の型で宣言するとエラーがでたんですね。
型が違うというエラーがでたらTypeNameで調べて修正していきます。


10308 : 小川慶一の回答 (2018-08-03 00:04:53)

mokoさん:

こんばんは。

以下のプログラムを実行し、自分でも試しにいろいろ書いて、実行し、としてください。

それから、ご自身の投稿を見直し、それに対してコメントを返してみてください。

Sub VarTest()
    'バリアント型変数には、配列を入れることもできます。
    'TypeName関数は、引数として渡された変数がどんなデータを持っているか?を教えてくれる関数
    'IsArray関数は、引数として渡された変数が配列かどうか?を教えてくれる関数
    
    Dim vNor As Variant '「As Variant を省略しても、Variant型で宣言したものとみなされる」ということでしたね。ピンとこない場合は発展編1第1章の復習を!
    Debug.Print "1"
    Debug.Print TypeName(vNor)
    Debug.Print IsArray(vNor)
    Debug.Print ""
    
    ReDim vNor(2)
    Debug.Print "2"
    Debug.Print TypeName(vNor)
    Debug.Print IsArray(vNor)
    Debug.Print ""
    
    vNor(0) = "A"
    vNor(1) = "B"
    vNor(2) = "C"
    Debug.Print "3"
    Debug.Print TypeName(vNor)
    Debug.Print IsArray(vNor)
    Debug.Print ""
    
    vNor = "hoge"
    Debug.Print "4"
    Debug.Print TypeName(vNor)
    Debug.Print IsArray(vNor)
    Debug.Print ""
End Sub
Sub ShoryakuTest()
    '変数宣言時に、 As Variant をつけないでみました。
    'それでも、Variant型で宣言したとみなされますね。
    
    Dim vNor '「As Variant を省略しても、Variant型で宣言したものとみなされる」ということでしたね。ピンとこない場合は発展編1第1章の復習を!
    Debug.Print "1"
    Debug.Print TypeName(vNor)
    Debug.Print IsArray(vNor)
    Debug.Print ""
    
    ReDim vNor(2)
    Debug.Print "2"
    Debug.Print TypeName(vNor)
    Debug.Print IsArray(vNor)
    Debug.Print ""
    
    vNor(0) = "A"
    vNor(1) = "B"
    vNor(2) = "C"
    Debug.Print "3"
    Debug.Print TypeName(vNor)
    Debug.Print IsArray(vNor)
    Debug.Print ""
    
    vNor = "hoge"
    Debug.Print "4"
    Debug.Print TypeName(vNor)
    Debug.Print IsArray(vNor)
    Debug.Print ""
End Sub


> 小川先生
> いつもお世話になっています。
>
> KeysメソッドとItemメソッドも配列を返すのに、変数で宣言するのがしっくりきていません。
> 配列を返すのなら配列を宣言するのでは?と思い、試しにやってみたらうまく動いたのですが、動いたら動いたでredimしてないのに?とか
> 配列の型は?とかいろいろ疑問が出てきました。
>
> 連想配列は通常の配列とは違うから・・・という風に思っておけばいいのでしょうか?


10307 : mokoさんのコメント (2018-08-02 18:07:23)

小川先生
いつもお世話になっています。

KeysメソッドとItemメソッドも配列を返すのに、変数で宣言するのがしっくりきていません。
配列を返すのなら配列を宣言するのでは?と思い、試しにやってみたらうまく動いたのですが、動いたら動いたでredimしてないのに?とか
配列の型は?とかいろいろ疑問が出てきました。

連想配列は通常の配列とは違うから・・・という風に思っておけばいいのでしょうか?

Sub test()
    Dim dic As Scripting.Dictionary
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim c As Long
    Dim cMx As Long
    Dim st As String
    cMx = Range("A" & Rows.Count).End(xlUp).Row
    
    For c = 1 To cMx
        st = Range("A" & c).Value
        If dic.Exists(st) Then
            dic.Item(st) = dic.Item(st) + 1
        Else
            dic.Add (st), 1
        End If
    Next
    
    '★疑問1 vKeysとvItmsを動的配列で宣言しても同じ結果になった
    '★疑問2 配列のサイズがわからなかったのでredimしていないがエラーが出なかった
    '★疑問3 vKeysの型をstring,vItmsの型をlongにしたら『型が一致しません』エラーになる
    Dim vKeys() As Variant
    Dim vItms() As Variant
    
    vKeys() = dic.Keys
    vItms() = dic.Items
    
    For c = LBound(vKeys) To UBound(vKeys)
        Range("C1").Offset(c).Value = vKeys(c)
        Range("D1").Offset(c).Value = vItms(c)
    Next
End Sub


10077 : 小川慶一の回答 (2018-06-06 00:27:12)

近藤さん:

scripting.dictionary, 便利ですよ。
後半に追加した補講動画、演習でも取り扱っています。お楽しみください☆

> 最初すごさがピンときませんでしたが、
> 理解すると驚き!めちゃめちゃ強力な技ですね!!!


10074 : 近藤さんのコメント (2018-06-05 22:46:42)

最初すごさがピンときませんでしたが、
理解すると驚き!めちゃめちゃ強力な技ですね!!!


3日がかりのその仕事、3分で終わらせる方法教えます。ガラパゴスタディーオンライン講座 ユーザー登録

本講座の動画一覧

  1. 【動画1】 式と戻り値1-データ型のおさらい
    【動画1】 式と戻り値1-データ型のおさらい 未習得
  2. 【動画2】 式と戻り値2-「式」と「戻り値」
    【動画2】 式と戻り値2-「式」と「戻り値」 未習得
  3. 【動画3】 式と戻り値3-「式」と「戻り値のデータ型」
    【動画3】  式と戻り値3-「式」と「戻り値のデータ型」 未習得
  4. 【動画4】 式と戻り値4-「式」とは(その1)
    【動画4】 式と戻り値4-「式」とは(その1) 未習得
  5. 【動画5】 式と戻り値5-「式」とは(その2)
    【動画5】 式と戻り値5-「式」とは(その2) 未習得
  6. 【動画6】 式と戻り値6-戻り値を返す式、戻り値を返さない式
    【動画6】  式と戻り値6-戻り値を返す式、戻り値を返さない式 未習得
  7. 【動画7】 コレクションとインデックス
    【動画7】  コレクションとインデックス 未習得
  8. 【動画8】 配列
    【動画8】 配列 未習得
  9. 【動画9】 静的配列
    【動画9】 静的配列 未習得
  10. 【動画10】 動的配列1-動的配列の基本
    【動画10】 動的配列1-動的配列の基本 未習得
  11. 【動画11】 動的配列2-Preserveキーワード
    【動画11】 動的配列2-Preserveキーワード 未習得
  12. 【動画12】 動的配列3-演習
    【動画12】 動的配列3-演習 未習得
  13. 【動画13】 多次元配列の基本
    【動画13】 多次元配列の基本 未習得
  14. 【動画14】 多次元動的配列
    【動画14】 多次元動的配列 未習得
  15. 【動画15】 多次元配列のサイズを調べる
    【動画15】 多次元配列のサイズを調べる 未習得
  16. 【動画16】 配列操作の便利関数、オプション、その他
    【動画16】  配列操作の便利関数、オプション、その他 未習得
  17. 【動画17】 連想配列1-Dictionaryオブジェクトの仕様
    【動画17】 連想配列1-Dictionaryオブジェクトの仕様 未習得
  18. 【動画18】 連想配列2-Dictionaryオブジェクトの使用例
    【動画18】 連想配列2-Dictionaryオブジェクトの使用例 未習得
  19. 【動画19】 連想配列3-Microsoft Scripting Runtimeのへの参照設定
    【動画19】 連想配列3-Microsoft Scripting Runtimeのへの参照設定 未習得
  20. 【動画20】 「名前付き引数」と「名前なし引数」
    【動画20】 「名前付き引数」と「名前なし引数」 未習得
  21. 【動画21】 省略可能な引数とその初期値
    【動画21】 省略可能な引数とその初期値 未習得
  22. 【動画22】 引数のデータ型
    【動画22】 引数のデータ型 未習得
  23. 【動画23】 Functionプロシージャ
    【動画23】 Functionプロシージャ 未習得
  24. 【動画24】 引数がひとつまたは複数のFunctionプロシージャ
    【動画24】 引数がひとつまたは複数のFunctionプロシージャ 未習得
  25. 【動画25】 Functionプロシージャ演習中
    【動画25】 Functionプロシージャ演習中 未習得
  26. 【動画26】 引数として配列を受け取るFunctionプロシージャ
    【動画26】 引数として配列を受け取るFunctionプロシージャ 未習得
  27. 【動画27】 引数を受け取らないSubプロシージャと引数つきSubプロシージャ
    【動画27】 引数を受け取らないSubプロシージャと引数つきSubプロシージャ 未習得
  28. 【動画28】 Subプロシージャの基本構造
    【動画28】 Subプロシージャの基本構造 未習得
  29. 【動画29】 モジュールレベル変数との使い分け
    【動画29】 モジュールレベル変数との使い分け 未習得
  30. 【動画30】 補講1-ディクショナリーオブジェクトのさらなる活用
    【動画30】 補講1-ディクショナリーオブジェクトのさらなる活用 未習得
  31. 【動画31】 補講2-ディクショナリーオブジェクトの .Count プロパティ
    【動画31】 補講2-ディクショナリーオブジェクトの .Count プロパティ 未習得
  32. 【動画32】 補講3-連想配列によるオブジェクトへの参照設定 - 新しいキーに設定する場合
    【動画32】 補講3-連想配列によるオブジェクトへの参照設定 - 新しいキーに設定する場合 未習得
  33. 【動画33】 補講4-連想配列によるオブジェクトへの参照設定 - 既存の値を上書きする場合
    【動画33】 補講4-連想配列によるオブジェクトへの参照設定 - 既存の値を上書きする場合 未習得
  34. 【動画34】 補講5-複数セルの集合を作るための便利なメソッド2つ
    【動画34】 補講5-複数セルの集合を作るための便利なメソッド2つ 未習得
  35. 【動画35】 補講6-連想配列演習-取引先ごとに取引金額の合計を算出する
    【動画35】 補講6-連想配列演習-取引先ごとに取引金額の合計を算出する 未習得
  36. 【動画36】 補講7-ウォッチ式の活用
    【動画36】 補講7-ウォッチ式の活用 未習得
  37. 【動画37】 補講8-ウォッチ式で配列の状態を確認する
    【動画37】 補講8-ウォッチ式で配列の状態を確認する 未習得
  38. 【動画38】 補講9-たった1行のコードでセルのデータを配列に投入する
    【動画38】 補講9-たった1行のコードでセルのデータを配列に投入する 未習得

塾長 小川慶一

メニュー

コメント紹介

もっと見る

ページの先頭へ