9

What I'm aiming to do is to create an object which is initialized with a hash and then query this object in order to get values from that hash. To make things clearer here's a rough example of what I mean:

class HashHolder
  def initialize(hash)
    @hash = hash
  end

  def get_value(*args)
    # What are my possibilities here?
  end
end

holder = HashHolder.new({:a => { :b => { :c => "value" } } } )
holder.get_value(:a, :b, :c) # should return "value"

I know I can perform iteration on the arguments list as in:

def get_value(*args)
  value = @hash
  args.each do |k|
    value = value[k]
  end
  return value
end

But if I plan to use this method a lot this is going to degrade my performance dramatically when all I want to do is to access a hash value.

Any suggestions on that?

3 Answers 3

19

To update the answer since it's been a while since it was asked.

(tested in ruby 2.3.1)

You have a hash like this:

my_hash = {:a => { :b => { :c => "value" } } }

The question asked:

my_hash.get_value(:a, :b, :c) # should return "value"

Answer: Use 'dig' instead of get_value, like so:

my_hash.dig(:a,:b,:c) # returns "value"

Since the title of the question is misleading (it should be something like: how to get a value inside a nested hash with an array of keys), here is an answer to the question actually asked:

Getting ruby hash values by an array of keys

Preparation:

my_hash = {:a => 1, :b => 3, :d => 6}
my_array = [:a,:d]

Answer:

my_hash.values_at(*my_array) #returns [1,6]
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the "misleading" answer - that was exaclty what I searched :)
4
def get_value(*args)
  args.inject(@hash, &:fetch)
end


In case you want to avoid iteration at lookup (which I do not feel necessary), then you need to flatten the hash to be stored:

class HashHolder
  def initialize(hash)
    while hash.values.any?{|v| v.kind_of?(Hash)}
      hash.to_a.each{|k, v| if v.kind_of?(Hash); hash.delete(k).each{|kk, vv| hash[[*k, kk]] = vv} end}
    end
    @hash = hash
  end
  def get_value(*args)
    @hash[args]
  end
end

3 Comments

Very elegant but it still iterates over the args array. Is there any way it can be done without iteration?
@sawa, I have implemented appr. this in our system some time ago, I have a question. Your solution with inject is it quicker than using each and iterate through sub hashes? At the end I assume it's almost doing the same thing?
Alomost the same. You cannot get faster than this unless you do my second solution.
-3

If you know the structure of the hash is always in that format you could just do:

holder[:a][:b][:c]

... returns "value".

Comments

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.