# Comparing Dictionaries of Dictionaries

**URL:** https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323
**Category:** General Usage
**Tags:** question, dictionaries
**Created:** [February 16, 2024, 10:42pm UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323 "2024-02-16T22:42:27Z")
**Posts on this page:** 17
**Page:** 1

<div class="post-metadata">

### Author: ![brombo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brombo/32/47867_2.png) [@brombo](https://discourse.julialang.org/u/brombo)
#### Post date: [February 16, 2024, 10:42pm UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/1 "2024-02-16T22:42:27Z")

</div>

I am trying to compare two dictionaries to see if they are equal. In the code the dictionaries are `S1.sdops` and S2.sdops

function (==)(D1::Sdop,D2::Sdop)::Bool  
if string(D1.s) != string(D2.s)  
return false  
end  
keys1 = keys(D1.sdops)  
keys2 = keys(D2.sdops)  
vkeys1 = collect(keys1)  
vkeys2 = collect(keys2)  
if vkeys1 != vkeys2  
return false  
end  
for key1 in vkeys1  
A = D1.sdops[key1]  
i2 = findall(x-\>x==key1,vkeys2)  
B = D2.sdops[vkeys2[i2[1]]]  
if A != B  
return false  
end  
end  
return true  
end

When I test “vkeys1 == vkeys2” is true but if take a key from vkeys1, say key1, and use it in in S2.dops, say S2.dops[key1], I get an error. That is why I calculate “i2” so I can use the correct entry in “vkeys2.” Is there a simpler way to compare the two dictionaries that what I am doing now? Note that the keys are themselves dictionaries.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [February 16, 2024, 11:30pm UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/2 "2024-02-16T23:30:45Z")

</div>

Whats is wrong with `D1.sdops == D2.sdops` ?  
Julia compares dictionaries by comparing all keys and values already.

---

<div class="post-metadata">

### Author: ![brombo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brombo/32/47867_2.png) [@brombo](https://discourse.julialang.org/u/brombo)
#### Post date: [February 17, 2024, 12:17am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/3 "2024-02-17T00:17:09Z")

</div>

Because it tells me the dictionaries are not equal when they are. Note that if I use string(D1.sdops) == string(D2.sdops) that works. If I print S1.sdops I get -

Dict{Pdop, Any}(Pdop(Dict{Any, Any}(y =\> 1, x =\> 1)) =\> 1)

I have defined “==” for the “Pdop” structure and that works.

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 17, 2024, 12:26am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/4 "2024-02-17T00:26:52Z")

</div>

Please enclose your code in triple backticks to make it easier to read

````julia
```
like this
```

````

Please show two dicts that you want to be equal so we can see why `==` says they are not.

---

<div class="post-metadata">

### Author: ![brombo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brombo/32/47867_2.png) [@brombo](https://discourse.julialang.org/u/brombo)
#### Post date: [February 17, 2024, 12:57am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/5 "2024-02-17T00:57:09Z")

</div>

Printing out the two dictionaries -

```julia
D1.sdops = Dict{Pdop, Any}(Pdop(Dict{Any, Any}(y => 1, x => 1)) => 1)
D2.sdops = Dict{Pdop, Any}(Pdop(Dict{Any, Any}(y => 1, x => 1)) => 1)

```

Note that `D1.sdops == D2.sdops` is `false` but `string(D1.sdops) == string(D2.sdops)` is `true`. For the struct `Pdop` I defined the operator `==` and when tested that works. The thing I noticed is that when I used a key from `D1.sdops` to reference an entry in `D2.sdops` I got an error.

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 17, 2024, 1:01am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/6 "2024-02-17T01:01:36Z")

</div>

Ok I can’t see the problem yet. Next step please create a minimal runnable example that demonstrates the problem. Something that I can just paste into my REPL and it will run.

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 17, 2024, 1:19am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/7 "2024-02-17T01:19:25Z")

</div>

It seems like your code (reformatted below) is relying on the key order of Dicts. Dict order is not guaranteed so you can’t rely on it. It’s also making string comparisons, I’m not sure why.

```julia
function (==)(D1::Sdop,D2::Sdop)::Bool
    if string(D1.s) != string(D2.s)
        return false
    end
    keys1 = keys(D1.sdops)
    keys2 = keys(D2.sdops)
    vkeys1 = collect(keys1)
    vkeys2 = collect(keys2)
    if vkeys1 != vkeys2
        return false
    end
    for key1 in vkeys1
        A = D1.sdops[key1]
        i2 = findall(x->x==key1,vkeys2)
        B = D2.sdops[vkeys2[i2[1]]]
        if A != B
            return false
        end
    end
    return true
end

```

---

<div class="post-metadata">

### Author: ![brombo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brombo/32/47867_2.png) [@brombo](https://discourse.julialang.org/u/brombo)
#### Post date: [February 17, 2024, 1:41am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/8 "2024-02-17T01:41:30Z")

</div>

I am attaching as minimal as I can make it. I don’t use the REPL. I use the geany editor and run the program in the editor `julia ./minimal.jl`. I am using the Sybolics.jl symbolic algebra package. `Pdop` is a generalize partial derivative operator and `Sdop` is a scalar differential operator (linear combination of `Pdop`’s and scalar coefficients which could be algebraic expressions). I am working on converting what I originally did here in Python into Julia. -

[https://galgebra.readthedocs.io/en/latest/](https://galgebra.readthedocs.io/en/latest/)

[minimal.jl](https://discourse.julialang.org/uploads/short-url/3RNogAGbFjBoO3zaP3jF5TjwzAM.jl) (4.72 KB)

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 17, 2024, 2:04am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/9 "2024-02-17T02:04:15Z")

</div>

Deleting your implementations of `==` and using  
`AutoHashEquals.@auto_hash_equals` on `struct Pdop` seems to work. But you do have to be careful about mutating hashable values.

---

<div class="post-metadata">

### Author: ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)
#### Post date: [February 17, 2024, 2:23am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/10 "2024-02-17T02:23:09Z")

</div>

> [@brombo](#):
>
> ```julia
> D1.sdops = Dict{Pdop, Any}(Pdop(Dict{Any, Any}(y => 1, x => 1)) => 1)
> D2.sdops = Dict{Pdop, Any}(Pdop(Dict{Any, Any}(y => 1, x => 1)) => 1)
> 
> ```
> 
> Note that `D1.sdops == D2.sdops` is `false` but `string(D1.sdops) == string(D2.sdops)` is `true`. For the struct `Pdop` I defined the operator `==` and when tested that works. The thing I noticed is that when I used a key from `D1.sdops` to reference an entry in `D2.sdops` I got an error.

I think your problem is that you haven’t defined a custom `hash` for `Pdop` (@jar1 beat me to it), and the default method computes different values for these two keys since they wrap dicts of different identity, even if the wrapped dicts happen to have the same content. Here’s a minimal example showing how to define `hash` and `==` to make this work:

```julia
struct Foo{D<:Dict}
    d::D
end

f1 = Foo(Dict('a' => 1))
f2 = Foo(Dict('a' => 1))
@show(f1)
@show(f2)
# Output:
# f1 = Foo{Dict{Char, Int64}}(Dict('a' => 1))
# f2 = Foo{Dict{Char, Int64}}(Dict('a' => 1))
# They look equal. Do they compare equal?

@show(hash(f1))
@show(hash(f2))
@show(f1 == f2)
# Output:
# hash(f1) = 0xf856fde93fedf01f
# hash(f2) = 0x206a57ed32981578
# f1 == f2 = false
# Nope---different hashes and not ==

# Define custom hash and ==
Base.hash(a::Foo, h::UInt) = hash(a.d, h)
Base.:(==)(a::Foo, b::Foo) = isequal(a.d, b.d)

@show(hash(f1))
@show(hash(f2))
@show(f1 == f2)
# Output:
# hash(f1) = 0x08186ea4a12ae8a5
# hash(f2) = 0x08186ea4a12ae8a5
# f1 == f2 = true
# There we go! Same hash and ==

```

I suppose this is what `@auto_hash_equals` does for you.

As @jar1 points out, be careful with this. Once such a `Pdop` instance has been used as a dict key, the dict wrapped inside it must never be mutated, otherwise the outer dict would be rendered unusable.

---

<div class="post-metadata">

### Author: ![brombo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brombo/32/47867_2.png) [@brombo](https://discourse.julialang.org/u/brombo)
#### Post date: [February 17, 2024, 2:44am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/11 "2024-02-17T02:44:27Z")

</div>

Thank both of you very much. Neither my `Pdop` or `Sdop` structs are mutable.

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 17, 2024, 3:11am UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/12 "2024-02-17T03:11:32Z")

</div>

Yeah but the dict inside it is mutable, and changes to that dict might affect the hash of the outer container.

---

<div class="post-metadata">

### Author: ![brombo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brombo/32/47867_2.png) [@brombo](https://discourse.julialang.org/u/brombo)
#### Post date: [February 17, 2024, 5:01pm UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/13 "2024-02-17T17:01:52Z")

</div>

Is using copy on the dictionary enough or should I do deepcopy?

---

<div class="post-metadata">

### Author: ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)
#### Post date: [February 17, 2024, 6:04pm UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/14 "2024-02-17T18:04:19Z")

</div>

Definitely deepcopy. The hash of the dict may depend on the actual content of any mutable containers nested inside it. For example:

```julia-repl
julia> d = Dict('a' => [1, 2, 3])
Dict{Char, Vector{Int64}} with 1 entry:
  'a' => [1, 2, 3]

julia> hash(d)
0x8f166356a55c01fc

julia> push!(d['a'], 4);

julia> hash(d) # Not the same as before
0x1c2f622f4dcb3ed6

```

But a better question is perhaps whether you should really be using an object that wraps a dict (or any other mutable container) as a key. Is it possible to make `Pdop` wrap a `NamedTuple` instead of a dict? Or simply extract the data from the dict and store them in dedicated fields in the `Pdop` struct?

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 17, 2024, 6:08pm UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/15 "2024-02-17T18:08:02Z")

</div>

My view on `deepcopy` is that `deepcopy` shouldn’t exist and any users should explicitly define their own `copy` methods instead.

> [@danielwe](#):
>
> But a better question is perhaps whether you should really be using an object that wraps a dict (or any other mutable container) as a key.

Agreed.

---

<div class="post-metadata">

### Author: ![brombo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brombo/32/47867_2.png) [@brombo](https://discourse.julialang.org/u/brombo)
#### Post date: [February 17, 2024, 6:34pm UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/16 "2024-02-17T18:34:23Z")

</div>

You are both telling me that for my case be very careful using dictionaries of better yet find an alternative. I will find an alternative.

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 17, 2024, 6:52pm UTC](https://discourse.julialang.org/t/comparing-dictionaries-of-dictionaries/110323/17 "2024-02-17T18:52:54Z")

</div>

The actual rule is `a == b implies hash(a) == hash(b)` for all `a` and `b`. You need to make sure that always holds. If you define `hash` and `==` or use AutoHashEquals.jl which defines them for you, it should be fine as long as you don’t then change the values later.

A guideline is once you hash a value, you must not mutate it afterwards. Looking quickly at your code I don’t notice any violations of that. So I’d guess you’re fine with a Dict and applying AutoHashEquals.jl to your structs.

The only complicating factor is that this code uses Symbolics.jl which does something special with `==`. I don’t know much about Symbolics.jl so I can’t really help with that part.
