Linking two values in either direction


#1

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


#2

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.


#3

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


#4

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"

#5

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:

Cheers,

Colin


#6

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


#7

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


#8

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.


#9

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


#10

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

#11

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


#12

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

#13

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:

Cheers,

Colin


#14

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.