Sets and Dictionaries in Julia

In the past week, we’ve covered the basics of Julia, as well as looked at all the primitive types’ implementations. Today we’ll wind up the week with a brief discussion of the modern types in Julia

Dictionaries

We saw our first julian dictionary early on in the article on Looping in an example of for loop. As promised, here’s a more complete exposition on dictionaries. First of all, the syntax is slightly different:

julia> dict=Dict("machine"=>1,"geek"=>3,"learning"=>2,"geek"=>"awesome")
Dict{String,Any} with 3 entries:
  "geek"     => "awesome"
  "machine"  => 1
  "learning" => 2

The use of => for associative arrays was initiated by Perl, where the reason was to make key-value association clearer. Moreover, in Julia too, dictionaries are hashable, hence, the compiler can cache them.

One might wonder though, why not use -> like WolframLanguage and Scala ? The reason is that keeping in with the mathematical notation, Julia uses that token for maps as we’ll see in the last section today. Thus, one can read the dictionary as a tuple of associations( indicated by token that looks like the mathematical symbol for implication sign ). You access the value associated with a key just like in python:

julia> dict["geek"]
"awesome"

Or you can retrieve a value, while checking if it’s there (similar to how you’d use the get property in Python):

julia> get(dict,"MLG","got no value yet")
"got no value yet"

Note that the above won’t mutate the dictionary dict. If you want check if a key is there in the dictionary, and if absent, associate the default value to that key, you’d use get!:

julia> dict
Dict{String,Any} with 3 entries:
  "geek"     => "awesome"
  "machine"  => 1
  "learning" => 2

julia> get!(dict,"hey",-2)
-2

julia> dict
Dict{String,Any} with 4 entries:
  "geek"     => "awesome"
  "hey"      => -2
  "machine"  => 1
  "learning" => 2

Assigning values is similar to python as well:

julia> dict["hi"]=-1
-1

julia> dict
Dict{String,Any} with 5 entries:
  "geek"     => "awesome"
  "hi"       => -1
  "hey"      => -2
  "machine"  => 1
  "learning" => 2

As you may have guessed, you may also specify the type of a dictionary

julia> typedDict=Dict{Int,String}()
Dict{Int64,String}()

julia> typedDict[0]="MLG"
"MLG"

julia> typedDict["MLG"]=1 #This will give an error due to type issues
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
Closest candidates are:
  convert(::Type{T}, ::T) where T<:Number at number.jl:6
  convert(::Type{T}, ::Number) where T<:Number at number.jl:7
  convert(::Type{T}, ::Ptr) where T<:Integer at pointer.jl:23
  ...
Stacktrace:
 [1] setindex!(::Dict{Int64,String}, ::Int64, ::String) at ./dict.jl:372
 [2] top-level scope at REPL[15]:1

On that note, remember that one you initialize a dictionary without specifying the types, Julia infers the appropriate types, and they become fixed. For example, the first dictionary we declared earlier, now must have it’s keys to be of type String

julia> dict[1]="MLG"
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type String
...

Some other methods are demonstrated below:

julia> keytype(dict) #what type of keys are expected
String

julia> haskey(dict,"MLG") # does a given dictionary have the specified key
false

julia> delete!(dict,"hey") #delete a key if present, else do nothing
Dict{String,Any} with 4 entries:
  "geek"     => "awesome"
  "hi"       => -1
  "machine"  => 1
  "learning" => 2

julia> pop!(dict,"hi")
-1

julia> keys(dict) # lets one iterate over the keys
Base.KeySet for a Dict{String,Any} with 3 entries. Keys:
  "geek"
  "machine"
  "learning"

julia> values(dict) # lets one iterate over values
Base.ValueIterator for a Dict{String,Any} with 3 entries. Values:
  "awesome"
  1
  2

julia> pairs(dict) #lets one iterate over key value pairs
Dict{String,Any} with 3 entries:
  "geek"     => "awesome"
  "machine"  => 1
  "learning" => 2

You might have noticed that some functions have an exclamation mark. Though unenforced, this signifies that the function is changing the object passed into it. We’ll talk more about this in an upcoming article devoted to functions.

Set

Sets in julia are implemented as instances of Base.Set. We can construct an empty set as:

julia> ϕ=Set()
Set{Any}()

Or use other objects to create sets:

julia> sa=Set([1 2 2]) #Directly creating a set of integers from an array
Set{Int64} with 2 elements:
  2
  1

julia> sm=Set([ 2 3 4; 5 6 7]) #Using a matrix to create a set of integers
Set{Int64} with 6 elements:
  7
  4
  2
  3
  5
  6

julia> sd=Set(dict) #Using a dictionary to create a set of tuples
Set{Pair{String,Any}} with 3 elements:
  Pair{String,Any}("learning", 2)
  Pair{String,Any}("geek", "awesome")
  Pair{String,Any}("machine", 1)

We can carry out set theoretic operations arbitrarily:

julia> union(sa,sm) #Union
Set{Int64} with 7 elements:
  7
  4
  2
  3
  5
  6
  1

julia> union(sa,sd) #Intersection
Set{Any} with 5 elements:
  Pair{String,Any}("learning", 2)
  2
  Pair{String,Any}("geek", "awesome")
  Pair{String,Any}("machine", 1)
  1

julia> intersect(sa,sd) #Intersection of sets of different type
Set{Int64}()

julia> setdiff(sm,sa) #Difference of sets
Set{Int64} with 5 elements:
  7
  4
  3
  5
  6

julia> symdiff(sd,sa) #Symmetric difference of sets
Set{Any} with 5 elements:
  Pair{String,Any}("learning", 2)
  2
  Pair{String,Any}("geek", "awesome")
  Pair{String,Any}("machine", 1)
  1

We can also have set theoretic conditions

julia> sa ⊆ sm
false

julia> issubset(sa,sm)
false

julia> sa ⊊ sm 
false

Note that ⊂ is not defined for sets in Julia.

To end the week, I implore you to read the documentation and find out how you would check if two sets are disjoint (Hint: first update to the latest version of Julia).

References

Leave a Reply

Your email address will not be published.