Nested dictionary usage

Hi,

I am trying to create a nested dictionary where integer tuple keys map to another dictionary with an integer key and a float value. When I try to add values to this data structure, I get a key error

Test  = Dict{Tuple{Int64,Int64},Dict{Int64,Float64}}()
Test[(1,1)][1] = 1.0
Stacktrace:
 [1] getindex(::Dict{Tuple{Int64,Int64},Dict{Int64,Float64}}, ::Tuple{Int64,Int64}) at .\dict.jl:477
 [2] top-level scope at In[89]:3

If I create the data structure manually like so

Test2 = Dict( (1,1) => Dict(1 => 1.0))

I get the data structure I tried to create above

Dict{Tuple{Int64,Int64},Dict{Int64,Float64}} with 1 entry:
  (1, 1) => Dict(1=>1.0)

and you can retrieve the value like I tried to add a value initially

Test2[(1,1)][1]
1.0

If I try to add another value though, I get an error.

Test2[(1,2)][2] = 2.0
KeyError: key (1, 2) not found

Stacktrace:
 [1] getindex(::Dict{Tuple{Int64,Int64},Dict{Int64,Float64}}, ::Tuple{Int64,Int64}) at .\dict.jl:477
 [2] top-level scope at In[93]:1

Any help or guidance would be appreciated.

Your first dictionary has no keys or values in it (it’s blank). So you can’t index it at [(1,1)] at all in the first place.

julia> D = Dict{Tuple{Int64,Int64},Dict{Int64,Float64}}()
Dict{Tuple{Int64,Int64},Dict{Int64,Float64}} with 0 entries

julia> D[(1,1)]
ERROR: KeyError: key (1, 1) not found
Stacktrace:
 [1] getindex(::Dict{Tuple{Int64,Int64},Dict{Int64,Float64}}, ::Tuple{Int64,Int64}) at ./dict.jl:477
 [2] top-level scope at REPL[940]:1

julia> D[(1,1)] = Dict{Int, Float64}()
Dict{Int64,Float64} with 0 entries

julia> D[(1,1)][1] = 1.0
1.0

julia> D
Dict{Tuple{Int64,Int64},Dict{Int64,Float64}} with 1 entry:
  (1, 1) => Dict(1=>1.0)

To do something like D[key][1] = v, the value in D[key] must already be defined (since you’re indexing into it)

3 Likes

Thank you. Your answer works.
It juat takes an extra few lines to execute what I was trying to do.

using DataStructures
using DataFrames
df = DataFrame(Tkey1 = [1,1,2,2,3,4],Tkey2 = [1,1,1,2,1,1],
IntKey = [1,2,1,1,1,1],Value = [1.0,2.0,4.0,2.2,3.0,4.1])

D = Dict{Tuple{Int64,Int64},Dict{Int64,Float64}}()
for i in 1:size(df)[1]
    D[(df.Tkey1[i],df.Tkey2[i])] = Dict{Int64,Float64}()
end
D
Dict{Tuple{Int64,Int64},Dict{Int64,Float64}} with 5 entries:
  (3, 1) => Dict{Int64,Float64}()
  (2, 2) => Dict{Int64,Float64}()
  (1, 1) => Dict{Int64,Float64}()
  (4, 1) => Dict{Int64,Float64}()
  (2, 1) => Dict{Int64,Float64}()
for i in 1:size(df)[1]
     D[(df.Tkey1[i],df.Tkey2[i])][df.IntKey[i]] = df.Value[i]
end
D
Dict{Tuple{Int64,Int64},Dict{Int64,Float64}} with 5 entries:
  (3, 1) => Dict(1=>3.0)
  (2, 2) => Dict(1=>2.2)
  (1, 1) => Dict(2=>2.0,1=>1.0)
  (4, 1) => Dict(1=>4.1)
  (2, 1) => Dict(1=>4.0)

It may be more convenient to use a DefaultDict:

julia> using DataStructures
julia> d = DefaultDict{Tuple{Int64,Int64},Dict{Int64,Float64}}(()->Dict{Int64,Float64}())
DefaultDict{Tuple{Int64,Int64},Dict{Int64,Float64},var"#8#9"} with 0 entries

julia> d[(1,1)]
Dict{Int64,Float64} with 0 entries

julia> d[(1,2)][3]=4
4

julia> d
DefaultDict{Tuple{Int64,Int64},Dict{Int64,Float64},var"#8#9"} with 2 entries:
  (1, 2) => Dict(3=>4.0)
  (1, 1) => Dict{Int64,Float64}()
4 Likes

Sorry for the slow reply.
Thank you very much. Your solution is very useful.