【问题标题】:How to set dynamically value of nested key in Ruby hash如何在 Ruby 哈希中动态设置嵌套键的值
【发布时间】:2017-05-29 02:32:44
【问题描述】:

这应该很容易,但我找不到合适的解决方案。 对于第一级键:

resource.public_send("#{key}=", value)

但是对于foo.bar.lolo

我知道我可以像下面这样得到它:

'foo.bar.lolo'.split('.').inject(resource, :send)

resource.instance_eval("foo.bar.lolo")

但是假设我不知道嵌套级别,如何将值设置为最后一个变量,它可能是第二或第三。

是否有针对所有级别的通用方法? 对于我的示例,我可以这样做:

resource.public_send("fofo").public_send("bar").public_send("lolo=", value)

【问题讨论】:

标签: ruby-on-rails ruby hash send


【解决方案1】:

出于好奇,请回答哈希:

hash = { a: { b: { c: 1 } } }
def deep_set(hash, value, *keys)
  keys[0...-1].inject(hash) do |acc, h|
    acc.public_send(:[], h)
  end.public_send(:[]=, keys.last, value)
end

deep_set(hash, 42, :a, :b, :c)
#⇒ 42
hash
#⇒ { a: { b: { c: 42 } } }

【讨论】:

  • 这不是和hash#bury基本一样吗?即this
  • @maxple 我不知道bury 是什么。这是纯红宝石。
  • 这就像挖掘的辅助工具。就像我链接的宝石。只是说人们通常称此功能为埋葬。
  • @maxple 我认为您应该添加该链接以及使用它的示例作为答案。询问场外资源是题外话,但在答案中给出它们不是题外话。
  • @mudasobwa 非常感谢,我进行了修改以满足我的哈希值,它可以工作 keys[0...-1].inject(resource) { |acc, key| acc.public_send(key) }.public_send("#{keys.last}=", value)
【解决方案2】:

默认情况下,ruby 中的哈希不会为您提供这些点方法。

您可以链接发送调用(这适用于任何对象,但您不能以这种方式正常访问哈希键):

  "foo".send(:downcase).send(:upcase)

在使用嵌套哈希时,可变性这个棘手的概念是相关的。例如:

  hash = { a: { b: { c: 1 } } }
  nested = hash[:a][:b]
  nested[:b] = 2
  hash
  # => { a: { b: { c: 2 } }

这里的“可变性”是指当你将嵌套散列存储到一个单独的变量中时,它实际上仍然是指向原始散列的指针。可变性对于这种情况很有用,但如果您不理解它,它也会产生错误。

您可以将:a:b 分配给变量,使其在某种意义上“动态”。

还有更高级的方法可以做到这一点,例如较新的 Ruby 中的 dig

versions.

  hash = { a: { b: { c: 1 } } }
  keys_to_get_nested_hash = [:a, :b]
  nested_hash = hash.dig *keys_to_get_nested_hash
  nested_hash[:c] = 2
  hash
  # => { a: { b: { c: 2 } } }

如果你使用OpenStruct,那么你可以给你的哈希点方法访问器。老实说,链接send 电话并不是我经常使用的。如果它可以帮助您编写代码,那就太好了。但是您不应该发送用户生成的输入,因为它不安全。

【讨论】:

  • hash.public_send(:[], :a).public_send(:[]=, :b, 42) 完美运行,没有什么棘手的。
  • @AlekseiMatiushkin on version 3.0.3 here 我发现它仅在您已在哈希中预定义键时才有效,例如:{}.public_send(:[], :a).public_send(:[]=, :b, 42) 获取 undefined method `[]=' for nil:NilClass
【解决方案3】:

虽然您可以实现一些方法来按照您现在设置的方式执行操作,但我强烈建议您重新考虑您的数据结构。

为了澄清您的一些术语,您示例中的key 不是键,而是方法调用。在 Ruby 中,当你有像 my_thing.my_other_thing 这样的代码时,my_other_thing 总是一个方法,而不是一个键,至少不是这个术语的正确含义。

确实,您可以通过以这种方式链接对象来创建类似哈希的结构,但这样做确实有代码味道。如果您认为foo.bar.lolo 是一种在哈希中查找嵌套的lolo 键的方法,那么您可能应该使用常规哈希。

x = {foo: {bar: 'lolo'}}
x[:foo][:bar] # => 'lolo'
x[:foo][:bar] = 'new_value' # => 'new_value'

此外,虽然可以通过这种方式使用 send/instance_eval 方法,但这并不是最佳做法,甚至会产生安全问题。

【讨论】:

    猜你喜欢
    • 2015-03-28
    • 2020-11-21
    • 1970-01-01
    • 2020-02-14
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    • 2020-01-30
    • 2019-04-30
    相关资源
    最近更新 更多