访问嵌套哈希值

时间:2016-03-07 22:01:34

标签: ruby hash

我试图过滤嵌套的哈希并提取各种键和值。这是我正在查看的哈希:

exp = {
  fam: {cty: "bk", ins: 3},
  spec: {cty: "man", ins: 2},
  br: {cty: "qns", ins: 1},
  aha: {cty: "man", ins: 0}
}

我试图找到cty"man"的所有哈希密钥。我想运行结果为以下哈希的东西:

e = {
spec: {cty: "man", ins: 2}, 
aha: {cty: "man", ins: 0}
}

我尝试了这个,看起来它几乎可以工作:

exp.each do |e, c, value|
  c = :cty.to_s
  value = "man"
  if e[c] == value
    puts e
  end
end

但我得到的结果是:

=> true 

而不是我正在寻找的东西:

e = {
spec: {cty: "man", ins: 2}, 
aha: {cty: "man", ins: 0} 
}

4 个答案:

答案 0 :(得分:3)

首先,你需要了解哈希迭代会给你什么。

考虑一下:

exp = {
  fam: {cty: "bk", ins: 3},
  spec: {cty: "man", ins: 2},
  br: {cty: "qns", ins: 1},
  aha: {cty: "man", ins: 0}
}
exp.map { |e, c, value| [e, c, value] }
# => [[:fam, {:cty=>"bk", :ins=>3}, nil], [:spec, {:cty=>"man", :ins=>2}, nil], [:br, {:cty=>"qns", :ins=>1}, nil], [:aha, {:cty=>"man", :ins=>0}, nil]]

这基本上就是你在循环时所做的事情,而Ruby传递了键/值对。您告诉Ruby在e中提供当前哈希值,c中的当前哈希值,并且由于没有传递任何其他内容,因此{{1} }参数变为value

相反,您需要一个用于键的块变量,一个用于值:

nil

请注意,nil值已消失。

考虑到这一点重写您的代码,并为简单起见重构:

    exp.map { |k, v| [k, v] }
# => [[:fam, {:cty=>"bk", :ins=>3}], [:spec, {:cty=>"man", :ins=>2}], [:br, {:cty=>"qns", :ins=>1}], [:aha, {:cty=>"man", :ins=>0}]]

现在它返回了你想要的键,所以很容易抓住整个哈希。 select是您尝试查找特定内容时使用的合适方法:

exp = {
  fam:  {cty: 'bk',  ins: 3},
  spec: {cty: 'man', ins: 2},
  br:   {cty: 'qns', ins: 1},
  aha:  {cty: 'man', ins: 0}
}

exp.each do |k, v|
  if v[:cty] == 'man'
    puts k
  end
end

# >> spec
# >> aha

旧版本的Ruby没有维护散列迭代器的哈希输出,所以我们必须强制回哈希:

exp = {
  fam:  {cty: 'bk',  ins: 3},
  spec: {cty: 'man', ins: 2},
  br:   {cty: 'qns', ins: 1},
  aha:  {cty: 'man', ins: 0}
}

e = exp.select { |k, v| v[:cty] == 'man' }
# => {:spec=>{:cty=>"man", :ins=>2}, :aha=>{:cty=>"man", :ins=>0}}

答案 1 :(得分:2)

e = {}
exp.each do |k,v|
  if v[:cty] == "man"
    e[k] = v
  end
end

p e

甚至

e = exp.select do |k,v|
  v[:cty] == "man"  
end

答案 2 :(得分:0)

正如Tin Man指出的那样,在迭代哈希时,有两个参数可以传递给一个块(本例中为doend之间的代码)---一个用于它的关键和其他价值。 迭代哈希(并打印出它的值)

h = { a: "hello", b: "bonjour", c: "hola" }

使用.each方法,您可以执行以下操作:

h.each do |key, value|
  puts  value
end

结果将是:

hello
bonjour
hola
 => {:a=>"hello", :b=>"bonjour", :c=>"hola"}

请注意,“返回”值是我们迭代的哈希值,它在ruby中计算为true。 (nilfalse以外的任何内容都将在Ruby中评估为true。请参阅What evaluates to false in Ruby?

这很重要,因为您在代码中获得true的原因(实际上应该是{:fam=>{:cty=>"bk", :ins=>3}, :spec=>{:cty=>"man", :ins=>2}, :br=>{:cty=>"qns", :ins=>1}, :aha=>{:cty=>"man", :ins=>0}}),而不是您想要的解析哈希是.each返回的值散列的方法是散列本身(评估为true)。

这就是为什么osman创建了一个空哈希e = {},以便在哈希的每次迭代中,我们可以使用我们想要的键和值填充新创建的哈希e

这解释了为什么他能做到:

e = exp.select do |k,v|
  v[:cty] == "man"  
end

这里的代码依赖于select方法能够返回带有我们想要的键和值的新哈希(而不是像.each方法那样的原始哈希)。

但如果你这样做

e = exp.each do |k,v|
  v[:cty] == "man"  
end

变量e将被分配原始哈希exp本身,这不是我们想要的。因此,了解应用方法时返回值的内容非常重要。

有关返回值(以及一般Ruby)的更多信息,我强烈推荐LaunchSchool“Ruby编程简介”(https://launchschool.com/books/ruby)中的免费电子书。这不仅帮助我认识到返回值的重要性,而且还为我提供了一般性的Ruby prigramming的坚实基础,如果您计划学习Ruby on Rails(我现在正在做的话),这非常有用。) / p>

答案 3 :(得分:0)

挖掘嵌套哈希的最简单方法是: -

class Hash
  def deep_find(key, object=self, found=nil)
      if object.respond_to?(:key?) && object.key?(key)
         return object[key]
      elsif object.is_a? Enumerable
         object.find { |*a| found = deep_find(key, a.last) }
         return found
      end
  end
end

Hash.deep_find(key)