Dict can't find a key it already said is there?

I’m confused I have this piece of code (see below, the important part is the the push! … line), it will push a value to a set located in a dict at a key (called “coord”) that we have already verified exists in the dict using a “coord in dict.keys” test. Mostly this code runs well, but from time to time, it fails with an error message like **ERROR:** KeyError: key (487, 466) not found" (the actual numbers vary between the tests).

I’m very confused by this. Also I’m not very experienced with Julia, so there may easily be stupid things I’ve done :-).

The full source code can be found here: https://github.com/la3lma/julia-playground/blob/master/ParticleMovement.jl

To reproduce the error, run “runSmoketest(10)” after loading the module.

  function partition(particles::Set{ParticleState}, xdim::Int64, ydim::Int64, maxx::Float64, maxy::Float64)
      dict = Dict{Tuple{Int64, Int64}, Set{ParticleState}}()
      for p in particles
          coord = pointToImageCoord(p.pos, xdim, ydim, maxx, maxy)
      	  if (coord in dict.keys)
            push!(dict[coord], p)
          else
            newSet = Set{ParticleState}([p])
            dict[coord] = newSet
          end
      end
      return dict
   end

That is not how you do it, you do haskey(dict, coord). Remember that Julia doesn’t have classes so you basically never call methods like that.

4 Likes

Thank you so much! That solved the problem (and tought me a lesson .-) )

The right thing here would be keys(dict). Your dict.keys creates a strange behaviour as in the following example:

myD = Dict{Tuple{Int, Int}, Int}()

for i in 1:100
    myD[(rand(1:100), rand(1:100))] = rand(1:100)
end

julia> collect(pairs(myD))[1:5]
5-element Array{Pair{Tuple{Int64,Int64},Int64},1}:
 (76, 69) => 3 
 (19, 13) => 13
 (15, 23) => 93
 (68, 81) => 57
  (85, 3) => 47

everything fine so far, now with keys(myD):

for i in keys(myD)
    myD[i] = rand(1:100)
end

julia> collect(pairs(myD))[1:5]
5-element Array{Pair{Tuple{Int64,Int64},Int64},1}:
 (76, 69) => 2 
 (19, 13) => 1 
 (15, 23) => 34
 (68, 81) => 98
  (85, 3) => 51

still fine, now with your dict.keys:

for i in myD.keys
    myD[i] = rand(1:100)
end

julia> collect(pairs(myD))[1:5]
5-element Array{Pair{Tuple{Int64,Int64},Int64},1}:
                                   (15, 39) => 95 
 (7956008003829065844, 7022836293254017892) => 41 
 (3755189675490410920, 2097865092682005547) => 26 
  (128118608645467904, 5841654826181606699) => 85 
                      (-8, 140311374223048) => 100

julia> length(myD)
2142

Buuum, strange that that even worked so far … The reason seems to be that myD.keys gives something entirely different from keys(myD).

2 Likes

Shouldn’t “keys” be renamed to something like “key_slots” and then with Base.getproperty we can redefine “keys” to return only valid keys? I really like that all internals are exposed but this behavior seems to be very error prone.

You can actually remove in check and simplify whole construction to

push!(get!(dict, coord, Set{ParticleState}()), p)
2 Likes

Don’t use internals, use the API.

3 Likes