How to use haskey () and in () functions with complex dictionaries in Julia?

The haskey () and in () functions are very useful for checking the contents of dictionaries in Julia:

julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5)
Dict{String,Int64} with 5 entries:
  "c" => 3
  "e" => 5
  "b" => 2
  "a" => 1
  "d" => 4

julia> haskey(dict, "a")
true

julia> in(("a" => 1), dict)
true

      

but I was surprised by their behavior with complex keys:

julia> immutable MyT
           A::String
           B::Int64
       end

julia> a = Dict(MyT("Tom",191)=>1,MyT("Bob",20)=>1,MyT("Jo",315)=>1,MyT("Luc",493)=>1)
Dict{MyT,Int64} with 4 entries:
  MyT("Tom",191) => 1
  MyT("Jo",315)  => 1
  MyT("Bob",20)  => 1
  MyT("Luc",493) => 1

julia> keys(a)
Base.KeyIterator for a Dict{MyT,Int64} with 4 entries. Keys:
  MyT("Tom",191)
  MyT("Jo",315)
  MyT("Bob",20)
  MyT("Luc",493)

julia> haskey(a, MyT("Tom",191))
false

julia> in((MyT("Tom",191) => 1), a)
false

      

What have I done wrong? Thanks a lot for your comments!

Thanks to @Michael K. Borregaard, I can suggest this solution:

a = Dict{MyT, Int64}()

keyArray = Array{MyT,1}()
keyArray = [MyT("Tom",191),MyT("Bob",20),MyT("Jo",315),MyT("Luc",493)]

for i in keyArray
    a[i] = 1
end

println(a)
# Dict(MyT("Tom",191)=>1,MyT("Tom",191)=>1,MyT("Luc",493)=>1,MyT("Jo",315)=>1,MyT("Luc",493)=>1,MyT("Bob",20)=>1,MyT("Jo",315)=>1,MyT("Bob",20)=>1)

keyArray[1]            # MyT("Tom",191)
haskey(a, keyArray[1]) # true

      

But I have to store the keys in a separate array. This means it cannot guarantee the unity of the keys which are the power of dictionaries and why I chose to use it :(

So I have to use another step:

unique(keyArray)

      

Another better solution:

function CompareKeys(k1::MyT, k2::MyT)
    if k1.A == k2.A &&  k1.B == k2.B
        return true
    else 
        return false
    end
end

function ExistKey(k::MyT, d::Dict{MyT, Int64})
    for i in keys(d)
        if CompareKeys(k, i)
            return true
        end
    end
    return false
end

a = Dict(MyT("Tom",191)=>1,MyT("Bob",20)=>1,MyT("Jo",315)=>1,MyT("Luc",493)=>1)

ExistKey(MyT("Tom",192),a) # false

ExistKey(MyT("Tom",191),a) # true

      

Compared to Julia, Go is simpler for this problem:

package main

import (
    "fmt"
)

type MyT struct {
    A string
    B int
}

func main() {

    dic := map[MyT]int{MyT{"Bob", 10}: 1, MyT{"Jo", 21}: 1}

    if _, ok := dic[MyT{"Bob", 10}]; ok {
        fmt.Println("key exists")
    }
}
// answer is "key exists"

      

+3


source to share


4 answers


You just need to teach your type MyT

so that you will consider it equal in terms of its composite fields:



julia> immutable MyT
           A::String
           B::Int64
       end
       import Base: ==, hash
       ==(x::MyT, y::MyT) = x.A == y.A && x.B == y.B
       hash(x::MyT, h::UInt) = hash(x.A, hash(x.B, hash(0x7d6979235cb005d0, h)))

julia> a = Dict(MyT("Tom",191)=>1,MyT("Bob",20)=>1,MyT("Jo",315)=>1,MyT("Luc",493)=>1)
Dict{MyT,Int64} with 4 entries:
  MyT("Jo", 315)  => 1
  MyT("Luc", 493) => 1
  MyT("Tom", 191) => 1
  MyT("Bob", 20)  => 1

julia> haskey(a, MyT("Tom",191))
true

julia> in((MyT("Tom",191) => 1), a)
true

      

+6


source


There are many good answers here, I just wanted to add some subtlety: this is partly because it ==

calls ===

instead of recursively ==

when checking for structural equality, and partly because equal to ( ==

) strings are not currently identical to ( ===

). In particular, the fact that MyT("foo", 1) != MyT("foo", 1)

is that "foo" !== "foo"

.

Strings are only "immutable by convention" - they are technically mutable, but Julia does not disclose the API for mutating them and does not recommend mutating them. However, you can access and modify your base bytes, which allows you to write a program that allocates two lines by changing them rather than the other. This means that they cannot be ===

in the Henry Baker sense of the predicate "EGAL" (also here ). If you have an immutable type with only "primitive type" fields, this doesn't happen:



julia> immutable MyT2 # `struct MyT2` in 0.6
           A::Float64
           B::Int
       end

julia> x = MyT2(1, 1)
MyT2(1.0, 1)

julia> y = MyT2(1, 1)
MyT2(1.0, 1)

julia> x == y
true

julia> x === y
true

      

I have already suggested that we change this and ==

recursively call ==

. This has to be fixed, someone just has to get the job done. Moreover, in Julia 1.0, we could make Strings truly immutable, rather than just immutable by convention and therefore have meaning "foo" === "foo"

. I created an issue to discuss and track this change.

+3


source


You are creating a new object in the call haskey

. But the two objects created MyT("Tom", 191)

are just two different MyT objects with the same field values.

Instead, do

key1 = MyT("Tom", 191)
a = Dict(key1 => 1)
haskey(a, key1)

      

see also

key2 = MyT("Tom", 191)
key1 == key2 # false

      

A jela-ideomatic way of dealing with this would be to define a method ==

for MyT objects, so two objects are equal if they have the same field values. This will allow you to use them the way you do.

It depends on whether you want a type to be complex. Another simple and efficient way to do what you want is to use Tuple as a key:

a = Dict(("Tom", 191) => 1)
haskey(a, ("Tom", 191)) # true
a[("Tom", 191)] # 1

      

+2


source


My approach will be similar to Matt, but a little simpler (?). Tuples are perfectly valid dictionary keys, so I would simply overload the appropriate functions to convert your type back and forth to a tuple:

julia> immutable M; A::String; B::Int64; end
julia> import Base: =>, haskey, in
julia> =>(a::M, b) = (a.A, a.B)=>b
julia> haskey(a::Dict, b::M) = haskey(a, (b.A, b.B))
julia> in(a::Pair{M, Int64}, b::Int64) = in((a.first.A,a.first.B)=>a.second,b)

      

julia> a = Dict(M("Dick", 10)=>1, M("Harry", 20)=>2)
Dict{Tuple{String,Int64},Int64} with 2 entries:
  ("Dick", 10)  => 1
  ("Harry", 20) => 2

julia> haskey(a, M("Dick", 10))
true

julia> in(M("Dick", 10)=>1, a)
true

      

"Compared to Julia, Go is more direct about this problem."

True. It also tends to be more error prone (depending on your perspective). If you want to distinguish between two objects (used as keys) that do not correspond to the same object in memory, then Go's approach of simply testing "value equality" would get you in trouble here (although one could assert "value equality" usually makes sense when comparing "keys").

0


source







All Articles