什么是访问任意深度嵌套哈希值的最Ruby-ish方式?

8

如果给定了一个哈希值:

AppConfig = {
  'service' => {
    'key' => 'abcdefg',
    'secret' => 'secret_abcdefg'
  },
  'other' => {
    'service' => {
      'key' => 'cred_abcdefg',
      'secret' => 'cred_secret_abcdefg'
    }
  }
}

我需要一个函数,有时返回service/key,有时返回other/service/key。一种简单直接的方式是传入哈希和键数组,如下所示:

def val_for(hash, array_of_key_names)
  h = hash
  array_of_key_names.each { |k| h = h[k] }
  h
end

为了使此调用结果为“cred_secret_abcdefg”:
val_for(AppConfig, %w[other service secret])

看起来应该有比我在val_for()中编写的更好的方法。


如果找不到中间键,应该发生什么?如果最后一个键不存在呢? - tokland
4个回答

9
def val_for(hash, keys)
  keys.reduce(hash) { |h, key| h[key] }
end

如果找不到某个中间键,则会引发异常。请注意,这与keys.reduce(hash, :[])完全等效,但这可能会使一些读者感到困惑,我建议使用块。


当然,在我的实现中,调用者需要知道配置键是什么。谢谢! - JD.
是的,你的确更好 :)。 - Andrés

6
%w[other service secret].inject(AppConfig, &:fetch)

哦,确实,你甚至可以写 :fetch。如果最后一个键未找到,这也会失败,但对于 OP 来说似乎没问题。 - tokland
你的评论让我的回答看起来像是在你将符号添加到proc解决方案后才写的。 - sawa
我感到困惑,我点赞了你的回答,并用“哦,确实”进行了评论,恰好是为了表明我已经忘记了这个inject简化,并且在编辑我的回答之前想要给予认可!很显然,我没有达到我的意图 :-( - tokland
起初,我不理解你在评论中所说的“even”的含义。它暗示着与某些东西进行比较。然后我意识到,在阅读了你回答的编辑部分之后,你的评论才有意义。这似乎是假设读者会先看你的答案,然后再将我的答案视为其变体。但现在我理解了你的意图。 - sawa
1
好的。我所说的“even”是指你可以进一步简化它并且不需要使用&,因为inject接受一个符号,所以Symbol#to_proc技巧是不必要的。 - tokland

1
appConfig = {
  'service' => {
    'key' => 'abcdefg',
    'secret' => 'secret_abcdefg'
  },
  'other' => {
    'service' => {
      'key' => 'cred_abcdefg',
      'secret' => 'cred_secret_abcdefg'
    }
  }
}

def val_for(hash, array_of_key_names)
  eval "hash#{array_of_key_names.map {|key| "[\"#{key}\"]"}.join}"
end

val_for(appConfig, %w[other service secret]) # => "cred_secret_abcdefg"

谢谢Andres,但是eval字符串和使用转义字符并不比我最初编写的代码更美观。:\ - JD.

1
Ruby 2.3.0引入了一个名为dig的新方法,可以在HashArray上使用,完全解决了这个问题。请点击此处获取更多信息。
AppConfig.dig('other', 'service', 'secret')

如果键在任何层级上缺失,它将返回nil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接