# 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,

Colin

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}:
"a"=>:z
"foo"=>:bar

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"]
:z

julia> d[:z]
"a"
``````

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

Cheers,

Colin

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.

Cheers,

Colin

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.

Colin

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.

Cheers,

Colin

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)`

Cheers,

Colin

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

Cheers,

Colin

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.

2 Likes