Unable to access value in dict via known keys

I have a loop which

  • Starts with a vector input
  • Computes a value based on vector
  • Stores the value in a dict with the vector as the key
  • Randomly removes a value from the vector and replaces it with another from a distribution

At the end I try to access a value from the dict but get:

julia> haskey(results,collect(keys(results))[1])
false

Here is a full MWE:

function poprand!(v)
    """Remove random variable from vector v"""
    return splice!(v, rand(1:length(v)))
end

begin
    inputs = collect(1:4)
    results = Dict{Vector{Int64}, Float64}()
    for _ in 1:5
        inputs = collect(sort(tuple(inputs...)))
        results[inputs] = rand(1:100)
        @assert haskey(results,inputs)
        poprand!(inputs)
        push!(inputs, rand(1:100))
    end
end

haskey(results,collect(keys(results))[1]) # false

Why is this occurring?

I’m using version 1.10.0-beta2

I don’t know exactly what goes on but it’s related to mutating keys after they have been stored in the dictionary. Simpler MWE:

ulia> v = [1]
1-element Vector{Int64}:
 1

julia> d = Dict(v => 1)
Dict{Vector{Int64}, Int64} with 1 entry:
  [1] => 1

julia> haskey(d, v)
true

julia> haskey(d, [1])
true

julia> v[1] = 2
2

julia> d
Dict{Vector{Int64}, Int64} with 1 entry:
  [2] => 1

julia> haskey(d, v)
false

julia> haskey(d, [1])
false

julia> haskey(d, [2])
false

julia> haskey(d, first(keys(d)))
false
2 Likes

The hash value used to put the entry in the dictionary is computed from the key, and that does not change when you mutate the key. When you index with an equal value, it computes a different hash value and fails to find the key.

1 Like

I wondered about mutation but when viewing keys(results) and hash.(keys(results)) each value was unique whereas I thought this issue would mean all entries would mean there would be duplicates in hash/key (although this doesn’t make sense now I think about it).

However, adding a copy to the key (results[copy(inputs)] =...) makes the example work, so you’re right it is mutating keys.

But this copy based solution doesn’t seem ideal. Is there a better way?

Depends on what you mean with “better”. Using mutable values as keys and mutating them is inherently not good, so the only other option is to keep the old object around, so that you can access the value stored in the Dict later again. By definition, if you use a different object as a key, you’re going to get a different or no value at all.

Can you perhaps reword your algorithm, so that it doesn’t require storing mutable objects in a dictionary?

For the purposes of what I’m doing, I’ll just do results[tuple(inputs...)] which then gives me immutable keys.

1 Like

I’m more asking this than suggesting: is using IdDict here a good idea/solution? Any downsides to it?

1 Like