Linking two values in either direction

Hi all,

Is anyone aware of a type that can be used to link two values, in either direction? A trivial example would be if I wanted to link "a" to :z. I’d like to have some object obj such that obj("a") returns :z, and obj(:z) returns "a".

I guess I could build this using a type that nests two dictionaries (one for each direction), or else using a Vector{Tuple{T1, T2}}, although both these options become a bit tricky if T1 and T2 are of the same type (i.e. linking "a", and "z"). I thought it worth asking if there is already something pre-existing that solves this problem.

Cheers all,


One possible way to efficiently store multi key-values would be with Set.
For example,

d = Tuple([:a,:b])
setdiff(d, [:a])

Each collection holds the key and all possible values. One can obtain the values by taking the key out. You can adapt this process in various ways such as a Dict which uses the key to find the Set and returns the setdiff.

The simplest way would just be the flat file approach, you have an array of type Any(/etc) with two columns, pretty much like the Vector{Tuple{T1, T2}}, then find the one you want in one column (find-x or .==) and check the other column of that row.

It can get quite complicated/involved depending on what kind of link properties are needed

Sounds like a Dict would work:

julia> pairs = ["a" => :z, "foo" => :bar]
2-element Array{Pair{String,Symbol},1}:

julia> d = Dict(p for p in [pairs; reverse.(pairs)])
Dict{Any,Any} with 4 entries:
  :bar  => "foo"
  :z    => "a"
  "a"   => :z
  "foo" => :bar

julia> d["a"]

julia> d[:z]

Thanks for responding. Yes, that’s a neat way of doing things. After reading it, I was thinking of implementing something like this… until I saw DNF’s solution :slight_smile:



Thanks for responding. Yes, my first instinct was to do something like this. But now that I’ve seen DNF’s solution, I think I’ll go with that.



I had no idea you could mix types in a Dict like that! That is awesome. I guess the downside is that every link is stored twice, but I think this is more than made up for by the simplicity of the solution.

Cheers and thanks, I’ll definitely be using this.


It wouldn’t work in all cases, however, depending on what you want. If both "a" and "b" maps to :z then the later one wins.

Yes agreed - to be totally robust, you’d need to check all existing pairs before adding a new one. But for my purposes it is more than sufficient.



not only - there could resist single links too. And who will wins depends on internal Dict order not on order in vector of pairs:

julia> ppairs = ["a" => :z, "foo" => :bar, :z=>"c"]  # Julia 0.7 don't allow using pairs! :/ 
3-element Array{Pair{Any,Any},1}:
 Pair{Any,Any}("a", :z)    
 Pair{Any,Any}("foo", :bar)
 Pair{Any,Any}(:z, "c")    

julia> d = Dict(p for p in [ppairs; reverse.(ppairs)])
Dict{Any,Any} with 5 entries:
  "c"   => :z      # ("c", :z) is represented only with this direction
  :bar  => "foo"
  :z    => "a"  # first pair won
  "a"   => :z  # first pair won
  "foo" => :bar

Interesting point.

FYI, pairs work just fine for me on v0.7. I’m on Version 0.7.0-DEV.4379 (2018-02-24 23:50 UTC)



Maybe it changed after your version?

julia> versioninfo()
Julia Version 0.7.0-DEV.4439
Commit cde00cf* (2018-03-01 17:22 UTC)

julia> pairs = ["a" => :z, "foo" => :bar, :z=>"c"]
ERROR: cannot assign variable Base.pairs from module Main

Maybe… although various uses of pairs is mentioned quite a bit in the v0.7 release notes… it certainly doesn’t sound like the intention is to remove them.

I guess we’ll find out :slight_smile:



Cf this discussion about invertable dicts. TL;DR: this is a useful datastructure, and it would be great if someone made a PR to DataStructures.jl implementing it.