[[20230905113340]] 『dictionary の中身のデータを変更したい。』(栗栖) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『dictionary の中身のデータを変更したい。』(栗栖)

dictionary を使って以下のサンプルを作成しました。

Dim dic1 As Dictionary: Set dic1 = New Dictionary
Dim dat(3) As Variant
dat(1) = "1"
dat(2) = "2"
dat(3) = "3"
Call dic1.Add("A", dat)
dic1("A")(3) = dic1("A")(3) + 1

dic1("A")(3) の値は 4 になってほしいのですが
3 のままです。

?@.どうして変更できないのでしょうか?
?A.変更できる方法はありますか?

< 使用 Excel:Excel2019、使用 OS:Windows10 >


 >どうして変更できないのでしょうか?
 >変更できる方法はありますか?

 提示のような変更は出来なかったですね。

 MSは、Itemの値を取得、設定と言っているので、
 格納した配列そのものは変更出来ます。

 一旦、配列を取り出して、変更して、格納すればいいかと。

 https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/item-property-dictionary-object

(tkit) 2023/09/05(火) 11:57:20


Dim dic1 As Dictionary: Set dic1 = New Dictionary
Dim dat(3) As Variant
dat(1) = "1"
dat(2) = "2"
dat(3) = "3"
Call dic1.Add("A", dat)
dat(3) = dic1("A")(3) + 1
dic1("A") = dat

こんな感じです
( 'ふ') 2023/09/05(火) 13:10:04


ありがとうございました。
変更の方法はわかりました。
理解はできていませんけど...

(栗栖) 2023/09/05(火) 13:25:37


ウォッチウィンドウで値を確認するといいかもです。
あと、dic1.Add "A", dat でよいかと。

ところで、
dic1("A")(3) = dic1("A")(3) + 1
でエラーが出ない理由がわかりません。

(TZ) 2023/09/05(火) 13:36:05


 'ふ'さんの回答ですが、Dictionaryに格納された配列を変更しているのではなく、
 変数datの配列を変更していることになりますが、それでいいのですか?

 Dictionaryを使うということは複数の配列を格納したいということだと思いますので、
 例えば下記のように2つの配列を格納して最初の配列を変更する場合、

    Dim dic1 As Dictionary: Set dic1 = New Dictionary
    Dim dat(3) As Variant
    dat(1) = 1
    dat(2) = 2
    dat(3) = 3

    Call dic1.Add("A", dat)
    dat(1) = 4
    dat(2) = 5
    dat(3) = 6
    Call dic1.Add("B", dat)

    Debug.Print "変更前"; dic1("A")(1), dic1("A")(2), dic1("A")(3)

    dat(3) = dic1("A")(3) + 1
    dic1("A") = dat

    Debug.Print "変更後"; dic1("A")(1), dic1("A")(2), dic1("A")(3)

 イミディエイト結果
 変更前: 1 2 3
 変更後: 4 5 4

 変数datに最後に代入した値で上書きされます。
 本来は下記のようにならないとだめですよね。

 変更前: 1 2 3
 変更後: 1 2 4

(hatena) 2023/09/05(火) 13:50:21


 配列は動的配列にすれば、DictionaryのItemから配列を取り出せるので、
 下記のような感じになりますね。

    Dim dic1 As Dictionary: Set dic1 = New Dictionary
    Dim dat() As Variant
    ReDim dat(1 To 3)
    dat(1) = 1
    dat(2) = 2
    dat(3) = 3
    Call dic1.Add("A", dat)
    dat(1) = 4
    dat(2) = 5
    dat(3) = 6
    Call dic1.Add("B", dat)

    Debug.Print "変更前:"; Join(dic1("A"))

    dat = dic1("A")
    dat(3) = dat(3) + 1
    dic1("A") = dat

    Debug.Print "変更後:"; Join(dic1("A"))

(hatena) 2023/09/05(火) 13:59:03


 仕様と考えるしかないかもしれませんね。
 dat(3) の代わりに、中もdictionaryとすると可能ですけどね。

 Sub test2()
     Dim dic1 As Object
     Dim dic2 As Object
     Set dic1 = CreateObject("Scripting.Dictionary")
     Set dic2 = CreateObject("Scripting.Dictionary")

     dic2(1) = "1"
     dic2(2) = "2"
     dic2(3) = "3"
     Set dic1("A") = dic2

     dic1("A")(3) = dic1("A")(3) + 1
     Debug.Print dic1("A")(3)    ' => 4 が出力
 End Sub
(xyz) 2023/09/05(火) 14:04:29

 >理解はできていませんけど...

 理解したいんですかね?
 間違いのない回答は、
 「マイクロソフトの仕様なので、マイクロソフトに聞いてください」
 なんですよね。

 dic1("A")(3)って、
 dic1("A").Item(3)となり、Itemはプロパティです。
 なので、dic1("A").Itemの返り値(ここでは配列)のインデックス(3)の値が取得出来ます。

 推測ですが、
 Itemプロパティには、型関係無く格納できますので、
 引数は、Variant型なのでしょう。
 配列自体は格納出来るけど、要素なんて知らんよ、となりますよね?
(tkit) 2023/09/05(火) 14:05:32

 書いてる間に話がすすんじゃっているので、ちょっと出遅れ感がありますが

 こういうサンプルコードを考えると
    Sub sample1()
      Dim a(1 To 3) As Long
      Dim d As New Dictionary
      a(3) = 3
      d.Add "A", a
      Debug.Print d("A")(3)
      Erase a()
      Debug.Print d("A")(3)
      d("A")(3) = 4
      Debug.Print d("A")(3)
    End Sub

 もとの配列a()の変更はディクショナリに登録した配列に影響を及ぼさないことが確認できるので、
 ディクショナリに登録されている配列は元の配列の参照ではなく、コピーだと推察できる

 この場合、こういうことをやっているのと(たぶんほぼ)同じ

    Sub sapmle2()
       Dim a(1 To 3) As Long
       a(1) = 1
       a(2) = 2
       a(3) = 3
       ary a
       Debug.Print ary()(3)
       ary()(3) = ary()(3) + 1
       Debug.Print ary()(3)
    End Sub
    Function ary(Optional a As Variant)
       Static buf As Variant
       If Not IsMissing(a) Then buf = a
       ary = buf
    End Function

 なので、書き換わらない

 ディクショナリに登録するのがオブジェクトの場合は、オブジェクト変数はもともと参照なので問題がない
 例えばクラスの場合

    Sub sample3()
      Dim c As New Class1
      Dim d As New Dictionary
      c.a(3) = 3
      d.Add "A", c
      Debug.Print d("A").a(3)
      c.a(3) = 0
      Debug.Print d("A").a(3)
      d("A").a(3) = 4
      Debug.Print d("A").a(3)
    End Sub
    ' ---- Class1 -----
    Private m_a(1 To 3) As Long
    Public Property Get a(ByVal i As Long) As Long
        a = m_a(i)
    End Property
    Public Property Let a(ByVal i As Long, newValue As Long)
        m_a(i) = newValue
    End Property
 (xyz)さんのディクショナリの例の方がわかりやすかったですね
(´・ω・`) 2023/09/05(火) 14:17:06

 明解な説明ありがとうございます。
 そういうことかと推測していましたが、適切な説明例が思い浮かびませんでした。

(xyz) 2023/09/05(火) 14:26:29


 以上をふまえて、 
 Sub sample4()
    Dim d As New Dictionary
    Dim a(1 To 3) As Long, buf As Variant
    a(1) = 1
    a(2) = 2
    a(3) = 3
    d.Add "A", a
    Debug.Print d("A")(3)
    buf = d("A")           ' 配列のコピーが戻ってくる
    buf(3) = buf(3) + 1    ' d("A")(3) = d("A")(3) + 1 はここまでしかやってない
    d("A") = buf           ' ので書き戻す必要がある
    Debug.Print d("A")(3)
 End Sub
(´・ω・`) 2023/09/05(火) 15:06:20

    Dim v, a
    a = Array(1, 2, 3)
    For Each v In a
        v = v + 1
    Next

 vをいくら編集しようともaの中身が変わらない。
 (vがRangeだったらv.Valueの書き換えは出来る)

 当たり前だとわかっちゃいても、
 たまに「できたらいいのにな」と思ってしまう。 ^^;

(白茶) 2023/09/05(火) 15:21:16


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.