通过键获取ruby哈希值

时间:2012-03-04 00:42:11

标签: ruby arrays hash key

我有像这样的哈希,代表一个数据树

hash = {
    'key1' => {
         'sub1' => 1,
         'sub2' => 2
    },
    'key2' => 3
}

我想用一组表示路径的键来探索树。 一些例子:

用简单的路径:

keys = ['key2']

我想获得3

使用此路径:

keys = ['key1', 'sub1']

我想获得1

带有荣耀路径:

keys = ['key1', 'sub1', 'blabla']
keys = ['key1', 'blabla']

获取nil

等等......等等......你明白了

5 个答案:

答案 0 :(得分:7)

keys.inject(hash) {|acc, value| acc[value]}

答案 1 :(得分:4)

不进行任何错误检查,但

h = {'k1' => {'s1' => 1, 's2' => 2}, 'k2' => 3}
ks = ['k1', 's1']
ks.inject(h){|hh, k| hh[k]}  # => 1
['k1', 's2'].inject(h){|hh, k| hh[k]} # => 2
['k2'].inject(h){|hh, k| hh[k]} # => 3

答案 2 :(得分:2)

最好记住对象,所以让我们为Hash类添加一个功能

# in intialize or whatever
class Hash
  def find_path path #recursive
    key = path.first
    if _path.size == 1 # end of path => return value
      [key]
    elsif [key].kind_of?(Hash) # continue
      [key].find_path path[1..-1]
    else # have to continue but not a has => out
      nil
    end
  end

  def find_path path # not recursive
    _path = path.clone #copy to shift
    _tree = self #start with self
    while(_path.size > 1 && _tree) do #while not the end and _tree 
      _v = _tree[_path.shift]
      _tree = _v.kind_of?(Hash) ? _v : nil
    end
    _tree ? _tree[_path.first] : nil
  end
end

这样:

hash = {:v1 => {:v1.1 => "yes", :v1.2 => "false"}}
hash.find_path [:v1, :v1.1]
# => "yes"
hash.find_path [:v1]
# => {:v1.1 => "yes", :v1.2 => "false"}
hash.find_path [:v2]
# => nil
hash.find_path [:v1, :v1.3]
# => nil

答案 3 :(得分:1)

这是我的解决方案(它类似于proxygear,但它没有修补Hash,并且在没有args的情况下调用它时表现正常)。我认为它比两个基于注入的解决方案更好,因为如果你试图访问DNE的路径,它们会爆炸。

class TraversibleHash
  attr_accessor :hash

  def initialize(hash)
    self.hash = hash
  end

  def access(*keys)
    recursive_access keys, hash
  end

private

  def recursive_access(keys, hash)
    return nil if keys.empty?
    element = hash.fetch(keys.shift) { return nil }
    return element if keys.empty?
    return nil unless element.kind_of? Hash # <-- should maybe raise an error?
    recursive_access keys, element
  end
end

以下是测试套件:

describe TraversibleHash do
  let(:traversible) { TraversibleHash.new 'key1' => { 'sub1' => 1, 'sub2' => 2 }, 'key2' => 3, nil => 1 }

  it 'returns nil when invoked without args' do
    traversible.access.should be_nil
  end

  it 'returns nil when accessing nonexistent keys' do
    traversible.access('key3').should be_nil
    traversible.access('key2', 'key3').should be_nil
    traversible.access('key1', 'sub3').should be_nil
    traversible.access('key1', 'sub2', 'subsub1').should be_nil
    traversible.access('abc', 'def', 'ghi').should be_nil
  end

  it 'finds toplevel keys' do
    traversible.access('key2').should == 3
    traversible.access('key1').should == {'sub1' => 1, 'sub2' => 2}
  end

  it 'traverses nested hashes to find nested keys' do
    traversible.access('key1', 'sub1').should == 1
    traversible.access('key1', 'sub2').should == 2
    TraversibleHash.new(1=>{2=>{3=>4}}).access(1, 2, 3).should == 4
  end
end

答案 4 :(得分:0)

从Ruby 2.3.0开始,你可以使用Hash#dig,它就是这样:

hash.dig(*keys)

在迁移之前,您可以使用ruby_dig gem。