【问题标题】:Ruby: Use String to set hash valueRuby:使用字符串设置哈希值
【发布时间】:2015-05-25 14:51:30
【问题描述】:

我正在使用 Rails 4 并尝试通过字符串名称访问哈希变量。

例如,假设我有一个包含字段名称和地址的成员哈希的策略模型。

我希望能够将 policy_member_name 转换为 policy[:member][:name]。

但是,此字符串可能不止 3 个部分。我能够访问该值,但无法使用以下设置它:

  ret = obj
  keys.each do |key|
    ret = ret[key.to_sym]
  end
  ret

其中keys 是一个数组,例如['member', 'name'],obj 是对象,例如Policy.first。但是,此方法只会返回 policy[:member][:name] 的值,并且不允许我执行 policy[:member][:name]=。

感谢您的帮助。

编辑:

我希望能够调用policy.member_name 来获取policy[:member][:name] 并让policy.member_name= "Something" 设置policy[:member][:name]="Something"

【问题讨论】:

  • 你能给我们已知的输入和想要的输出吗?
  • 旁注:在我看来,您正在为一些可能不像您想象的那么重要的事情牺牲性能。即使是 OpenStruct Ruby class - 非常接近您想要的 - 也会遭受性能成本,因为它依赖于 method_missing ... 而且,它可能更令人困惑而不是有用 - 例如,当:policy.member = Users.find(1); policy.member_name = 'Joe'; puts policy.member 时会发生什么。 ..?
  • 我需要这个的原因是我使用 gem best_in_place 来编辑字段。您是建议根本不使用OpenStructs,还是只是建议不需要点符号?或者您是否知道另一种编辑OpenStructs 的方法?谢谢!

标签: ruby-on-rails ruby hash


【解决方案1】:

这很容易通过method_missing 实现:

class HashSnake

  def initialize(hash)
    @hash = hash
  end

  def method_missing(method, *args, &block)
    parts = method.to_s.split('_').map(&:to_sym)
    pointer = @hash
    parts.each_with_index do |key, i|             
      if pointer.key? key
        if pointer[key].is_a? Hash
            if (i +1 == parts.length)
                return pointer[key]
            else
                pointer = pointer[key]
            end
        else
          return pointer[key]
        end
      # Checks if method is a setter
      elsif key[-1] == '=' && pointer.key?(key[0..-2].to_sym)
        pointer[key[0..-2].to_sym] = args[0]
      end
    end
  end
end

obj = HashSnake.new(
  member: {
    foo: 'bar',
    child: {
      boo: 'goo'
    }
  }
)

obj.member_foo = 'bax'
puts obj.member_foo.inspect
# "bax"
puts obj.member_child_boo.inspect
# goo

【讨论】:

  • @Myst 的评论总结得很好。你真的需要这个吗?在性能方面有相当大的成本,我什至不想考虑堆栈跟踪是什么样的。
【解决方案2】:

由于您将只授予用户对某些哈希的读取或写入权限,因此创建将这些哈希(字符串)的名称映射到哈希对象的哈希不会有任何困难。例如:

h0 = { a: 1, b: 2 }
h1 = { c: 3, d: { e: 4, f: { g: 5 } } }

hash = { "h0"=>h0, "h1"=>h1 }

完成后,只需创建一种满足您需求的领域特定语言(“DSL”)即可。很可能有一些宝石可以开箱即用,或者可以根据您的要求进行修改。

这是一种可能性的示例。 (我建议您在阅读代码之前先查看示例。)

代码

class Array
  def h(hash)
    h = hash[first]
    if last.is_a? Array
      seth(h, *self[1..-2].map(&:to_sym), *last)
    else
      geth(h, *self[1..-1].map(&:to_sym))
    end
  end

  private

  def geth(h, *keys)
    keys.reduce(h) { |h,k| h[k] }
  end

  def seth(h, *keys, last_key, type, val)
    val =
      case type
      when "STR" then val.to_str
      when "INT" then val.to_int
      when "SYM" then val.to_sym
      when "FLT" then val.to_f
      end
    geth(h, *keys)[last_key] = val
  end
end

示例

以下是一开始介绍的h0h1hash

["h0", "b"].h(hash)
   #=> 2
["h1", "d", "f", "g"].h(hash)
   #=> 5

["h0", "b", ["INT", 3]].h(hash)
   #=> 3
h0 #=> {:a=>1, :b=>3}
["h1", "d", "f", "g", ["STR", "dog"]].h(hash)
   #=> "dog"
h1 #=> {:c=>3, :d=>{:e=>4, :f=>{:g=>"dog"}}, :g=>"dog"} 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-26
    • 2021-11-19
    • 2015-04-02
    • 2020-12-13
    • 2021-02-05
    • 2012-03-31
    • 1970-01-01
    相关资源
    最近更新 更多