How to determine if a dictionary has some key? - julia

I want to key into a dictionary, but Julia throws an exception if the key does not exist. To avoid the exception, I first have to check if they key exists in the dictionary.
I'm using this custom function for now:
function has_some_key(dict, key)
for (k, v) in dict
if k == key
return true
end
end
return false
end
Is there a better way to determine if a dictionary has a mapping for a given key?

haskey will check whether some collection has a mapping for a given key:
julia> d
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
julia> haskey(d, 1)
true
julia> haskey(d, 3)
false

Another way, which might be viable depending on your use case, is to use get to provide a default value in case the key is not present:
julia> d = Dict(1 => "one", 2 => "two")
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
julia> get(d, 1, "zero")
"one"
julia> get(d, 3, "zero")
"zero"
There's also get!, which will store the default value for the queried key as well.

Related

How to get the empty entries from a Dictionary in Julia?

I have some dictionary I have defined which might have values that are empty. Is there a quick way to check to see if any of my key value pairs contain empty entries?
julia> a = Dict(1=>[1,2], 4=>[3,4], 6=>[])
Dict{Int64, Vector{T} where T} with 3 entries:
4 => [3, 4]
6 => Any[]
1 => [1, 2]
You wanted a quick way thus I would recommend:
findall(isempty, a)
One possible concise solution it to use a comprehension and the isempty function which will check this for you as follows:
julia> [k for (k,v) in a if isempty(v)]
1-element Vector{Int64}:
6
julia> filter(p->isempty(p.second), a)
Dict{Int64, Vector{T} where T} with 1 entry:
6 => Any[]
filter takes a function as its first argument and can take a Dictionary as its second argument.
The function is given a Pair of key-value pairs, which has members first (the key) and second (the value). So here, we filter by an anonymous function that checks whether p.second, i.e. the value, isempty, and return only those Pairs where that returns true.
An equivalent, and perhaps better looking, way to do this is:
julia> filter(isempty∘last, a)
Dict{Int64, Vector{T} where T} with 1 entry:
6 => Any[]
And for completeness' sake, if you want to just "check to see if" any of them are empty, you can take a count instead:
if count(isempty∘last, a) > 0
dosomething()
end

How can I add a new key value pair to an existing dictionary?

I have some dictionary that already exists. I want to add a new key-value pair, but I do not want to create a new dictionary from scratch.
How can I add a new key-value pair to an existing dictionary in Julia?
Julia uses the common dict[key] = value syntax for setting a key-value pair:
julia> dict = Dict(1 => "one")
Dict{Int64,String} with 1 entry:
1 => "one"
julia> dict[2] = "two"
"two"
julia> dict
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
The same syntax will override a key-value pair if the key already exists:
julia> dict
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
julia> dict[1] = "foo"
"foo"
julia> dict
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "foo"
dict[key] = value is syntax for a setindex! call. Although not as common, you can call setindex! directly:
julia> dict = Dict(1 => "one")
Dict{Int64,String} with 1 entry:
1 => "one"
julia> setindex!(dict, "two", 2)
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"

Julia 1.0.0: What does the `=>` operator do?

I see this Stackoverflow code for =>, but when I search Julia 1.0.0 on-line help for "=>", I get zero hits.
replace!(x, 0=>4) # The last expression is the focus of this question.
In the REPL help I get:
help?> =>
search: =>
Pair(x, y)
x => y
Construct a Pair object with type Pair{typeof(x), typeof(y)}. The elements are stored in the fields first and second.
They can also be accessed via iteration.
See also: Dict
Examples
≡≡≡≡≡≡≡≡≡≡
julia> p = "foo" => 7
"foo" => 7
julia> typeof(p)
Pair{String,Int64}
julia> p.first
"foo"
julia> for x in p
println(x)
end
foo
7
What does => do in replace!(x, 0=>4)? Does it create a pair, a replacement of all zeros by fours, or what? Why do I seem to not find it in the Julia 1.0.0 on-line docs?
EDIT
Code added to help me understand #Bill's helpful answer below:
julia> x = [1, 0, 3, 2, 0]
5-element Array{Int64,1}:
1
0
3
2
0
julia> replace!(x, 0=>4)
5-element Array{Int64,1}:
1
4
3
2
4
Edit 2
Besides #Bill's accepted answer, I found #Steven's answer helpful as well. Sorry I could not check them both, but Bill's came in first and they both offered useful information.
"What does => do in replace!(x, 0=>4)? Does it create a pair, a replacement of all zeros by fours, or what?"
It creates a Pair. In the function replace, a Pair in the second argument position means the multiple dispatch of replace() chooses a version of the replace function where, given a numeric array or string x, all items within x fitting the first part of the Pair are replaced with an instance of the second part of the Pair.
You can check the REPL docs for replace for details.
This small example should show how "=>" makes a pair
julia> replace("julia", Pair("u", "o"))
"jolia"
julia> replace("julia", "u" => "o")
"jolia"
"=>" operator means "Change into"
so
julia> replace("hello world",'l' => 'z')
"hezzo worzd"
means Change the string "hello world" using "change" 'l' "into" 'z'
and producing the resultant string "hezzo worzd"
julia> replace( [1,2,3,4,5], 3 => 666 )
5-element Array{Int64,1}:
1
2
666
4
5

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

haskey() and in() functions are very useful to test the content 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 I did wrong ?
Thank you very much for your comments !
Thanks to #Michael K. Borregaard, I can propose 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 keys in a separate array. This means that can't warranty the unicity of the keys which is the strength of the dictionaries and why I choose 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 more straightforward 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"
You just need to teach your MyT type that you want it to consider equality 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
There are lots of good answers here, I'd just like to add a subtlety: this is partly because == calls === rather than recursively calling == when checking for structural equality, and partly because equal (==) strings are not generally identical (===) currently. Specifically, the fact that MyT("foo", 1) != MyT("foo", 1) is because "foo" !== "foo".
Strings are only "immutable by convention" – they are technically mutable, but Julia doesn't expose APIs for mutating them and encourages you not to mutate them. You can, however, access their underlying bytes and mutate that, which allows you to write a program that distinguishes two strings by getting by mutating one and not the other. That means that they cannot be === in the sense of Henry Baker's "EGAL" predicate (also here). If you have an immutable type with only "primitive" type fields, then this does not 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 proposed that we change this and have == recursively call ==. This should be fixed, someone just needs to do the work. Moreover, in Julia 1.0 we could make Strings truly immutable rather than merely immutable by convention, and therefore have "foo" === "foo" be true. I've created an issue to discuss and track this change.
You're creating a new object in the haskey call. But two objects created by 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 julia-ideomatic way to deal with this would be to define an == method for MyT objects, so two objects are equal if they have the same field values. That would allow you to use them like you do.
It depends whether you need the type to be complex. Another easy and performant way to do what you want is to use a Tuple as the key:
a = Dict(("Tom", 191) => 1)
haskey(a, ("Tom", 191)) # true
a[("Tom", 191)] # 1
My approach would be similar to Matt's, but a bit simpler(?). Tuples are perfectly valid dictionary keys, so I would simply overload the relevant 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 straightforward for this problem"
True. It also happens to be more error-prone (depending on your perspective). If you wanted to differentiate 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 have landed you in trouble here (though one could argue 'value equality' generally makes more sense when comparing 'keys').

How to initialize a dictionary in Julia?

When I tried to do:
d = {1:2, 3:10, 6:300, 2:1, 4:5}
I get the error:
syntax: { } vector syntax is discontinued
How to initialize a dictionary in Julia?
The {} syntax has been deprecated in julia for a while now. The way to construct a dict now is:
Given a single iterable argument, constructs a Dict whose key-value pairs are taken from 2-tuples (key,value) generated by the argument.
julia> Dict([("A", 1), ("B", 2)])
Dict{String,Int64} with 2 entries:
"B" => 2
"A" => 1
Alternatively, a sequence of pair arguments may be passed.
julia> Dict("A"=>1, "B"=>2)
Dict{String,Int64} with 2 entries:
"B" => 2
"A" => 1
(as quoted from the documentation, which can be obtained by pressing ? in the terminal to access the "help" mode, and then type Dict)

Resources