『シリアル値を使用したDictionaryについての質問』(半日悩んだ)
こんにちは。
仕事で使用するため時間を基準とした外部データを取り込み、
そのデータをマクロで処理しようと時間をkeyとしたDictionaryを使いました。
しかし
For t = 開始 to 終了 Step TimeSerial(0, 1, 0)
v = dic.Item(t)
としたところある日時のところでitemが取得できないという現象が起きました。
ここのデバッグでdic.Exists(t)を見てみたらTrueだったので、何故なのか悩みました。
(時間データはyyyy/m/d h:mm:00のシリアル値です)
結果として
For Each t in dic.keys
v = dic.Item(t)
で解決したのですが、あまり飲み込めていません。
この理由がわかる方はご教授願えませんでしょうか。
< 使用 Excel:Microsoft365、使用 OS:Windows10 >
> ここのデバッグでdic.Exists(t)を見てみたらTrueだったので、何故なのか悩みました。 tが keyになくても v = dic.Item(t) を実行した瞬間に tをkey , Emptyを itemとするデータが作成される仕様です。 ですから、dic.Exists(t)は True を返します。
(xyz) 2024/02/02(金) 18:21:16
情報不足なので私にはわかりません。 dictionaryにどういうキーを与えたのか、照会のときにどんなキーを与えたのかが不明です。 他の回答者の回答をお待ちください。 (xyz) 2024/02/02(金) 19:47:27
原因は不明ですが、方法として思いつくのは、 Format関数でStringに変換したものをキーにするという方法ですね。 これなら誤差は防止できるような気がします。 dictionaryのキーはStringのほうが効率もよいように思います。それでは。 (xyz) 2024/02/02(金) 20:14:02
状況としては、 1分毎のデータをキーにDictionaryに格納したのに、 1分毎に取り出そうとしたら、そのキーが存在しない、と言う展開になった。 それはおかしいではないか、って話ですよね(多分)
>For t = 開始 to 終了 Step TimeSerial(0, 1, 0)
そのステートメントは、厳密に1分毎を指定したことにならないです。 何故って、時刻データは小数値だからです。
開始(小数値)に1分(小数値)を加算して行ったら、小数演算誤差問題が付いて回るので、 キッチリ一致しないキーがボコボコ出ても不思議ではないってことです。
上記が的外れでしたら、そちらの状況をもっと詳しく説明してください。
(半平太) 2024/02/02(金) 20:25:19
やはり誤差で一致しないシリアル値が出てくるのですね。
すっきりしました。
これからは計算で誤差が出そうな時はFor Eachかxyzさんが書かれたStringに変換しようと思います。
xyzさん、半平太さん、ありがとうございました。
(半日悩んだ) 2024/02/03(土) 11:01:19
小数演算誤差が原因というのはありえるかもしれませんが、 下記の検証コードでは1分おきのシリアル値(VBAではDate型)では24時間以内の範囲では 誤差は出ないようでした。
Dim 開始 As Date, 終了 As Date 開始 = TimeSerial(0, 0, 0) 終了 = TimeSerial(24, 0, 0)
Dim t As Date For t = 開始 To 終了 Step TimeSerial(0, 1, 0) If Second(t) <> 0 Then Debug.Print "誤差: ", t End If Next
元データに抜けがあったいう可能性もあるのでは。
> ここのデバッグでdic.Exists(t)を見てみたらTrueだったので、何故なのか悩みました。
具体的にはどのように確認しましたか? エラーがでたときにイミディエイトで確認したのなら、 xyzさんが指摘したDictionaryの仕様上無意味です。
下記のような感じでデバッグコードを埋め込んで確認したのならいいのですが。
For t = 開始 to 終了 Step TimeSerial(0, 1, 0) If Not dic.Exists(t) Then MsgBox t & "は存在しません" v = dic.Item(t)
xyzさんが提案したFormat関数でStringに変換したものをキーにするという方法にしておけば 小数演算誤差に悩まずにすむとは思います。
(hatena) 2024/02/03(土) 12:15:57
Dim dic As Object, i&, d As Date, k, 開始 As Date, 終了 As Date, t As Date Set dic = CreateObject("Scripting.Dictionary") 'Date型 yyyy/m/d h:mm:00をキーとするDictionary登録 For i = 0 To 59 d = "2024/2/3 8:" & i & ":00" dic(d) = i Next Debug.Print "-----------ちゃんと登録されているか確認-----------" For Each k In dic Debug.Print "Key(" & TypeName(k) & "型)="; k, "Value="; dic(k) Next 開始 = "2024/2/3 8:00:00" 終了 = "2024/2/3 9:00:00" Debug.Print "-----------誤差補正なし Step TimeSerial(0, 1, 0)でキーから値を取り出してみる-----------" For t = 開始 To 終了 Step TimeSerial(0, 1, 0) Debug.Print "Key(" & TypeName(t) & "型)="; t, "Value="; dic(t) Next Debug.Print "-----------誤差補正あり Step TimeSerial(0, 1, 0)でキーから値を取り出してみる-----------" For t = 開始 To 終了 Step TimeSerial(0, 1, 0) d = Format(t, "yyyy/m/d h:mm:00") Debug.Print "Key(" & TypeName(d) & "型)="; d, "Value="; dic(d) Next End Sub (*) 2024/02/03(土) 12:42:52
前回の回答では、 Second(t)で丸められるのでだめでした。
下記だと20:43以降で誤差がでました。
Public Sub Test11() Dim 開始 As Date, 終了 As Date 開始 = TimeSerial(0, 0, 0) 終了 = TimeSerial(24, 0, 0) Dim t As Date, i As Long For t = 開始 To 終了 Step TimeSerial(0, 1, 0) If t <> TimeSerial(0, i, 0) Then Debug.Print t; TimeSerial(0, i, 0) '確認用 End If i = i + 1 Next
End Sub
(hatena) 2024/02/03(土) 13:19:41
> 下記だと20:43以降で誤差がでました。
私の環境(Win11、XL2010、365)でTest11を実行したら、直ぐ相違が出ましたけども。
0:05:00 0:05:00 0:10:00 0:10:00 0:11:00 0:11:00 0:14:00 0:14:00 0:15:00 0:15:00 : : : (半平太) 2024/02/03(土) 16:21:39
>間違えました。
何を間違えたんですか?
あと、何故TimeSerialなんですか? 質問では、こう言っているんですけどねぇ・・ ↓ >、(時間データはyyyy/m/d h:mm:00のシリアル値です)
(半平太) 2024/02/03(土) 22:46:42
ループを回すのに
dateadd()
を使うのがいいかもしれませんね。
開始から終了までで1分ごとのシリアル値をtに入れて
tが1時間たつごとにdebug.printしてみました。
マイクロソフトがうるう秒まできちんと内部実装してるかは知りませんが。
dateaddが一番いいんじゃないですかね。
Sub test()
Dim 開始 As Date
Dim 終了 As Date
Dim t As Date
Dim i As Long
i = 0
開始 = Now()
終了 = Now() + 2
Debug.Print 開始
Debug.Print 終了
Do While DateAdd("n", i, 開始) <= 終了
t = DateAdd("n", i, 開始) If (i Mod 60) = 0 Then Debug.Print t End If i = i + 1 Loop End Sub
(通りすがり) 2024/02/04(日) 03:17:00
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.