如何在 Ruby 中迭代深度未知的深度嵌套哈希

How to iterate over deep nested hash without known depth in Ruby

我有多个 YAML(本地化)文件。我解析它们并在 Ruby.

中转换为散列

例如,这是其中之一:

hello: Hallo
messages:
  alerts:
    yay: Da!
    no: Nein
  deep:
    nested:
      another:
        level:
          hi: Hi!
test: Test!

基本上,这看起来像是 Rails 应用程序中使用 YAML 的语言环境文件。

我要做的就是递归迭代这个Hash,得到key和value。这样我就可以像 Google 翻译一样从 API 端点逐一翻译值。我想将嵌套哈希保留在相同的模式中,以便 Rails 可以通过键找到。

我知道我可以使用嵌套循环,但不能保证嵌套哈希的数量是已知的。我如何递归地迭代这个散列以便我可以操纵值 (translate/replace)?

预期结果:(使用 API 调用 的翻译服务后)

hello: Hello
messages:
  alerts:
    yay: Yup!
    no: No
  deep:
    nested:
      another:
        level:
          hi: Hi!
test: Test!

到目前为止我尝试过的:

hash = YAML.load('de.yml') # parse source Deutsch locale 
new_hash = {}

hash.each |key, value| do
  new_hash[key] = translate_func(value) # here... translate value then assign very same key including parents.

  # Do more loops....
end

# Now write this new_hash to yaml file...

但这只是操纵hello而已。为了和其他人一起工作,我必须做一个循环。但是嵌套了多少键是未知的。

如何遍历语言环境散列的所有值并保持架构完整?

如果可能但不是强制性的,如果我们能在最终结果上保持键的顺序,我会很高兴。稍后在手动检查时找到丢失的密钥会很棒。

我对 ruby 很陌生。 我正在使用 Ruby 2.7.2

结论/解决

所有答案都是正确的,我都喜欢。但是,我希望能够同时控制键和值。不仅仅是通过价值观进行转变。因此,我接受了适合我需要的答案。我能够通过选择的答案实现我的意图。

您可以在哈希对象上使用 deep_transform_values! 来递归更改值。 (或者它的非破坏性版本 deep_transform_values returns 一个新的散列而不是改变原来的散列。)

hash.deep_transform_values! { |value| translate_func(value) }

注意:deep_transform_values! 是一种 Rails 方法。如果您不使用 Rails.

,请参阅源代码 here 获取灵感

因此,您想递归解析,直到没有更多级别可以解析。

它在软件中非常常见,被称为“递归”。到处搜索以了解更多信息 - 它会在您的旅程中一次又一次地出现。欢迎来到 ruby 顺便说一句!

至于你现在的实际问题。阅读 https://mrxpalmeiras.wordpress.com/2017/03/30/how-to-parse-a-nested-yaml-config-file-in-python-and-ruby/

而且,考虑 i18n gem。请参阅此答案 and the docs for the gem https://github.com/ruby-i18n/i18n 这可能会解决您处理国际化的问题,而无需深入了解处理 yaml 文件的细节。

如果你想要一个简单的非rails解决方案,那么你可以创建一个递归方法:

def recurse(hash)
  hash.transform_values do |v|
    case v
    when String
      v.reverse # Just for the sake of the example
    when Hash
      recurse(v)
    else
      v
    end
  end
end

输出:

{"hello"=>"ollaH", "messages"=>{"alerts"=>{"yay"=>"!aD", false=>"nieN"}, "deep"=>{"nested"=>{"another"=>{"level"=>{"hi"=>"!iH"}}}}}, "test"=>"!tseT"}

然而,这可能是重新发明轮子的情况 - 您可以使用 i18n gem 进行翻译,并使用 i18n-tasks 使用 Google 翻译 API.