Dictionaryオブジェクトでキーが存在しているのに値が取得できない

タグの編集
投稿者 ヤマダ  (社会人) 投稿日時 2023/7/26 16:32:58
現在Excelのマクロを組んでいます。

題名の通りなのですが、セルから取得した値をキーにして値を連番で設定し、そのキーの値を取り出したいのですが、上手くいきません。

    Type level_Data
        levelCd As String                     
        levelName As String                                
    End Type
 
    Dim levelCd As Object
    Set levelCd = CreateObject("Scripting.Dictionary")
    Dim levelNo As Long: levelNo = 1
    Dim keyChk As String
    Dim test_csv() As level_Data


    xlLastRow = Cells(Rows.Count, LEVEL_NAME_SELL).Row
    DtRowCnt = Worksheets("Sheet1").Cells(xlLastRow, "A").End(xlUp).Row
        
    For i = 2 To DtRowCnt
        If Not levelCd.Exists(Worksheets("Sheet1").Cells(i, "A").Value) Then
            levelCd.Add Key:=Worksheets("Sheet1").Cells(i, "A").Value, _
                             Item:=levelNo
            levelNo = levelNo + 1
        End If
    Next i

    For i = 2 To DtRowCnt
        keyChk = ""
        keyChk = Worksheets("Sheet1").Cells(i, "A")
        test_csv.levelName  = Worksheets("Sheet1").Cells(i, "A")
        test_csv.levelCd  = levelCd.Item(keyChk)
    Next i

セル値が数値の時に値が空白で帰ってくるのですが、数値はキーとして設定できないのでしょうか?
1や2がセル値にあり、それをキーとして設定し、その値を最終的には取り出せるようにしたいです。

よろしくお願いします。

投稿者 KOZ  (社会人) 投稿日時 2023/7/26 17:25:55
キーを数値で登録し、文字列で取り出そうとしているからでは?

以下のコードは VB6 ですが、数値の 1 と、文字列の "1" は別物になっています。 
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
dic.Add 1, "A"
dic.Add "1""B"
Debug.Print dic(1)
Debug.Print dic("1")
結果
A
B
 

登録するとき、セルの値を CStr で文字列に変換し、キーとして利用すれば良さそうです。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/7/26 18:18:24
> Set levelCd = CreateObject("Scripting.Dictionary")

CompareMode プロパティが未設定なので、キー比較は BinaryCompare モードのままですね。


> Dim test_csv() As level_Data

> test_csv.levelName  = Worksheets("Sheet1").Cells(i, "A")
> test_csv.levelCd  = levelCd.Item(keyChk)

これは文法的に不自然ではありませんか?

test_csv.levelName ではなく、
test_csv(i).levelName とかであればまだ分かりますが、
それにしたって ReDim 等が必要なはず…。


> 数値はキーとして設定できないのでしょうか?
いいえ、数値もキーにできます。
しかし、キーのデータ型がブレるのは、あまりお勧めしません。
キーは常に同じ型に揃えておいた方が無難です。

特に数値型や Empty が渡される可能性がある場合はなおのこと。


たとえば
 Key:="KEY_" & Worksheets("Sheet1").Cells(i, "A").Value
のように、固定のプレフィックスなどを付けるとか、
 Key:=CStr(Worksheets("Sheet1").Cells(i, "A").Value)
のように、キーを明示的に String 型に揃えておくなどです。

Excel の場合、未入力セルは Empty 値を返す点にも注意が必要です。

' 空文字列と 数値 0 は区別される 
Set x = CreateObject("Scripting.Dictionary")
x.Add ""   , "A"
x.Add 0    , "B"

' Empty は、空文字列や数値 0 と合致してしまう 
x.Add Empty, "C"  '実行時エラー '457': このキーは既にこのコレクションの要素に割り当てられています。 
Set y = CreateObject("Scripting.Dictionary")
y.Add Empty, "D"
y.Add ""   , "E"  '実行時エラー '457': このキーは既にこのコレクションの要素に割り当てられています。 
Set z = CreateObject("Scripting.Dictionary")
z.Add Empty, "F"
z.Add 0    , "G"  '実行時エラー '457': このキーは既にこのコレクションの要素に割り当てられています。 



Dim a As Variant: a = 0
Dim b As Variant: b = ""
Dim c As Variant: c = #12:00:00 AM# '「1899/12/30 00:00:00」 
Dim d As Variant: d = False
Dim e As Variant: e = Empty

Debug.Print "a="; TypeName(a)   'Integer 
Debug.Print "b="; TypeName(b)   'String 
Debug.Print "c="; TypeName(c)   'Date 
Debug.Print "d="; TypeName(d)   'Boolean 
Debug.Print "e="; TypeName(e)   'Empty 

Debug.Print "*** 下記は False となる(空文字列は、数値ゼロとは別の値)"
Debug.Print b = a   '"" は 0 ではない 
Debug.Print b = c   '"" は 正子 ではない 
Debug.Print b = d   '"" は False ではない 

Debug.Print "*** 下記は True となる(Empty は、空文字列と数値ゼロの両方と同一視される)"
Debug.Print e = a   'Empty は 0 と等しい 
Debug.Print e = b   'Empty は "" と等しい 
Debug.Print e = c   'Empty は 正子 と等しい 
Debug.Print e = d   'Empty は False と等しい 

Debug.Print "*** 下記は True となる(正子、False、数値 0 は、いずれも同一値とみなされる)"
Debug.Print a = c
Debug.Print a = d
Debug.Print a = e
Debug.Print c = d
Debug.Print c = e
Debug.Print d = e
投稿者 ヤマダ  (社会人) 投稿日時 2023/7/27 09:28:40
KOZさん

取り出すときの型まで考慮していませんでした。
キーをString型で登録することで、無事に思ったようにすることができました。
ありがとうございました。
投稿者 ヤマダ  (社会人) 投稿日時 2023/7/27 09:47:56
魔界の仮面弁士さん

>CompareMode プロパティが未設定なので、キー比較は BinaryCompare モードのままですね。
プロパティの設定を忘れていました。
大文字、小文字の区別は必要なので設定します。

>これは文法的に不自然ではありませんか?
失礼しました。
test_csv配列の要素数は他で宣言しており、その記述が抜けており困惑させてしまいました。
加えてかっこも抜けていました。
すみません。

>いいえ、数値もキーにできます。
しかし、キーのデータ型がブレるのは、あまりお勧めしません。
キーは常に同じ型に揃えておいた方が無難です。

特に数値型や Empty が渡される可能性がある場合はなおのこと。

Emptyが空文字列と合致するのは想像できていましたが、数値0と合致するのは驚きです。
今回はキーを明示的にString型に揃えておくことにします。
ありがとうございました。