-1

This is the hash I have:

color_hash = { sky: "blue", curtain: "blue", pavement: "black" }

Is there a way to change it to make two keys map to the same result, something like this:

color_hash = { sky or curtain: "blue", pavement: "black" }

I want to be able to use color_hash[:sky] and it would print blue, and I could use color_hash[:curtain] and it would print blue.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
appleLover
  • 14,835
  • 9
  • 33
  • 50

3 Answers3

1

No, there is no way to do so with a default hash implementation in Ruby. See Is Ruby pass by reference or by value?. If this was possible, the setter method []= on Hash would have to be implemented in such a way that allowed you to pass by reference.

You could implement something like this with some kludgy syntax and a subclass of Hash though... something like this (just a warning, this is still buggy and fairly untested, but gives you an idea as to what might be involved)

class FooHash < Hash
  attr_accessor :related

  def initialize(*args)
    @related = {}
    super
  end

  def []=(*args)
    raise ArgumentError.new("ArgumentError: wrong number of arguments (#{args.length} for 2)") unless args.length > 1
    value = args.pop
    keys = args.dup

    args.each_index do |index|
      key = keys.delete_at(index)
      if related[key]
        related[key].each do |related_key|
          continue if keys.include?(related_key)
          super(related_key, value)
        end
      else 
        related[key] = []
      end
      related[key] += keys
      related[key].uniq!
      keys.insert(index, key)
    end

    args.each do |arg|
      super(arg, value)
    end
  end
end

It would allow you to do something like this:

1.9.3-p550 :036 > hsh = FooHash.new
 => {}
1.9.3-p550 :037 > hsh[:foo, :bar] = :baz
 => :baz
1.9.3-p550 :038 > hsh[:foo]
 => :baz
1.9.3-p550 :039 > hsh[:bar]
 => :baz
1.9.3-p550 :040 > hsh[:foo, :blah] = "baz2"
 => "baz2"
1.9.3-p550 :041 > hsh[:foo]
 => "baz2"
1.9.3-p550 :042 > hsh[:bar]
 => "baz2"
1.9.3-p550 :043 > hsh[:blah]
 => "baz2"

Long story short though, trying to imitate pointer manipulation in Ruby is kind of gross, don't do it.

Community
  • 1
  • 1
photoionized
  • 5,092
  • 20
  • 23
  • Doing this would also break Ruby's default behavior of allowing an Array as a key which could have some pretty amazing side-effects. – the Tin Man Sep 28 '15 at 22:50
1

Even I understand correctly what you want to do, consider using a different data structure.

color_hash = { sky: "blue", pothole: "black", curtain: "blue",
               pavement: "black", roadblock: "red" }

You could use Enumerable#group_by:

color_hash.group_by(&:last).tap { |h| h.each_key { |k| h[k].map!(&:first) } }
  #=> {"blue"=>[:sky, :curtain], "black"=>[:pothole, :pavement],
  #    "red"=>[:roadblock]} 

or Hash#update:

color_hash.each_with_object({}) { |(k,v),h|
  h.update(v=>[k]) { |_,o,n| o+n } }
  #=> {"blue"=>[:sky, :curtain], "black"=>[:pothole, :pavement],
  #    "red"=>[:roadblock]}

This uses the form of Hash#update (aka merge!) that employs a block to determine the values of keys that are present in both hashes being merged. Here { |_,o,n| o+n } is that block, where (the local variable) _ is the key (rather than, say, k, to tell the reader that it's not used in the calculation), o ("old") is the value of the hash h for that key and n is the value of the hash being merged in for that key.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
0

Perhaps try this.

  $someHashContainer = {:curtain => "Blue", :sky => "Blue"}

  def update_variables(value)
    $someHashContainer[:curtain] = value
    $someHashContainer[:sky] = $someHashContainer[:curtain]
  end

  puts $someHashContainer

  update_variables("Green")
  puts $someHashContainer

  $someHashContainer[:curtain] = "Red"
  puts $someHashContainer

  update_variables("Yellow")
  puts $someHashContainer


'''
OUTPUT:
{:curtain=>"Blue", :sky=>"Blue"}
{:curtain=>"Green", :sky=>"Green"}
{:curtain=>"Red", :sky=>"Green"}
{:curtain=>"Yellow", :sky=>"Yellow"}
'''
Kat Russo
  • 469
  • 4
  • 8