3

I am looking for a way to have, I would say synonym keys in the hash.

I want multiple keys to point to the same value, so I can read/write a value through any of these keys.

As example, it should work like that (let say :foo and :bar are synonyms)

hash[:foo] = "foo"
hash[:bar] = "bar"
puts hash[:foo] # => "bar"

Update 1

Let me add couple of details. The main reason why I need these synonyms, because I receive keys from external source, which I can't control, but multiple keys could actually be associated with the same value.

3
  • 1
    can you add more context? what are you trying to achieve? Commented Apr 9, 2013 at 23:13
  • 1
    why are you doing this? if you want hash[:foo] returns "bar", you can create so. hash[:foo] = "bar", hash[:bar] = "foo". This question is meaningless to me! Commented Apr 9, 2013 at 23:35
  • @shime & sbagdat: Plee see update. Commented Apr 10, 2013 at 1:38

4 Answers 4

9

Rethink Your Data Structure

Depending on how you want to access your data, you can make either the keys or the values synonyms by making them an array. Either way, you'll need to do more work to parse the synonyms than the definitional word they share.

Keys as Definitions

For example, you could use the keys as the definition for your synonyms.

# Create your synonyms.
hash = {}
hash['foo'] = %w[foo bar]
hash
# => {"foo"=>["foo", "bar"]}

# Update the "definition" of your synonyms.
hash['baz'] = hash.delete('foo')
hash
# => {"baz"=>["foo", "bar"]}

Values as Definitions

You could also invert this structure and make your keys arrays of synonyms instead. For example:

hash = {["foo", "bar"]=>"foo"}
hash[hash.rassoc('foo').first] = 'baz'
=> {["foo", "bar"]=>"baz"}
Sign up to request clarification or add additional context in comments.

Comments

5

You could subclass hash and override [] and []=.

class AliasedHash < Hash
  def initialize(*args)
    super
    @aliases = {}
  end

  def alias(from,to)
    @aliases[from] = to
    self
  end

  def [](key)
    super(alias_of(key))
  end

  def []=(key,value)
    super(alias_of(key), value)
  end

  private
  def alias_of(key)
    @aliases.fetch(key,key)
  end
end

ah = AliasedHash.new.alias(:bar,:foo)

ah[:foo] = 123
ah[:bar] # => 123
ah[:bar] = 456
ah[:foo] # => 456

1 Comment

don't extend core ruby classes, use delegation!
1

What you can do is completely possible as long as you assign the same object to both keys.

variable_a = 'a'
hash = {foo: variable_a, bar: variable_a}

puts hash[:foo] #=> 'a'
hash[:bar].succ!
puts hash[:foo] #=> 'b'

This works because hash[:foo] and hash[:bar] both refer to the same instance of the letter a via variable_a. This however wouldn't work if you used the assignment hash = {foo: 'a', bar: 'a'} because in that case :foo and :bar refer to different instance variables.

2 Comments

This works as long as all value changes are limited to referenced object mutation methods (like succ! and replace). As soon as someone sets one of the aliased values via []= assignment the alias will be broken.
@Gavin Miller: Interesting idea, but rather limited, because i will have always to use references (and never values)
1

The answer to your original post is:

hash[:foo] = hash[:bar]

and

hash[:foo].__id__ == hash[:bar].__id__it

will hold true as long as the value is a reference value (String, Array ...) .


The answer to your Update 1 could be:

input.reduce({ :k => {}, :v => {} }) { |t, (k, v)| 
        t[:k][t[:v][v] || k] = v;
        t[:v][v] = k;
        t
    }[:k]

where «input» is an abstract enumerator (or array) of your input data as it comes [key, value]+, «:k» your result, and «:v» an inverted hash that serves the purpose of finding a key if its value is already present.

1 Comment

ops, replace t[:k][t[:v][v] || k] = v; with t[:k][k] = t[:k][t[:v][v]] || v;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.