DataStructures.jl icon indicating copy to clipboard operation
DataStructures.jl copied to clipboard

Handle alternative types as default in DefaultDict

Open alecloudenback opened this issue 5 years ago • 5 comments

This is different than #530, which is asking about handling missing as a key. This issue relates to enabling missing as the default value.

This works and returns 42.0:

dd_real = DefaultDict(42.0,Dict(i => i for i in 1:3))
dd_real[0]

But this errors:

dd_missing = DefaultDict(missing,Dict(i => i for i in 1:3))
dd_missing[0]
MethodError: Cannot `convert` an object of type Missing to an object of type Int64

alecloudenback avatar Feb 09 '20 17:02 alecloudenback

Testing just a bit more, it seems like dd_real works because 42.0 can be coerced into an Int. The issue related to having a default type that is not the same type.

This does not work:

dd_string = DefaultDict("abc",Dict(i => i for i in 1:3))
dd_string[0]

And errors with:

MethodError: Cannot `convert` an object of type String to an object of type Int64

alecloudenback avatar Feb 09 '20 17:02 alecloudenback

Looks like the change that would be needed would be to modify

getindex(d::DefaultDictBase, key) = get!(d.d, key, d.default)

to

getindex(d::DefaultDictBase, key) = get(d.d, key, d.default)

in here: https://github.com/JuliaCollections/DataStructures.jl/blob/3b66dbcb802b5257e5a1667d50bc13d8b01f8ac7/src/default_dict.jl#L69

Tracing this back to Base - because this line calls get!, (I think) base doesn't want to allow a default return value that is different than the values stored in the Dict with get! since it could mutate the dict. Base allows mixed types when calling just get.

Is there a reason to use get! instead of get in default_dict.jl?

I could submit pull request if that little change above sounds reasonable.

alecloudenback avatar Feb 09 '20 18:02 alecloudenback

Example of what I mean:

This errors:

d = Dict(i => i for i in 1:3)
get!(d,10,"abc") 

While this does not:

d = Dict(i => i for i in 1:3)
get(d,10,"abc") 

alecloudenback avatar Feb 09 '20 18:02 alecloudenback

indeed, I have just made that PR.

If I had known you were going to offer I would have waited. It would be good if you could review it.

oxinabox avatar Feb 09 '20 18:02 oxinabox

The following does work:

julia> dd_missing_u = DefaultDict(missing, Dict{Int, Union{Int,Missing}}(i => i for i in 1:3))
DefaultDict{Int64,Union{Missing, Int64},Missing} with 3 entries:
  2 => 2
  3 => 3
  1 => 1

julia> dd_missing_u[0]
missing

Not sure if that is enough for your needs.

I would like it if:

DefaultDict{Int, Union{Missing,Int}}(missing, Dict(i => i for i in 1:3))

worked, but it doesn't (right now)

oxinabox avatar Feb 17 '20 10:02 oxinabox

DefaultDict{Int, Union{Missing,Int}}(missing, Dict(i => i for i in 1:3))

now works (merged in https://github.com/JuliaCollections/DataStructures.jl/pull/730)

Reading through the discussion in https://github.com/JuliaCollections/DataStructures.jl/pull/576 I think we shouldn't change to get().

laborg avatar Oct 05 '23 05:10 laborg