在Ruby哈希中覆盖子对象

时间:2015-07-16 06:00:40

标签: ruby hash

我有一个嵌套哈希,比如说:

data = {
  a: {
    b: {
      c: {
        d: {
          e: :f
        }
      }
    }
  }
}

另一个哈希:

update = {g: :h}

和路径:

path = [:a, :b, :c, :d]

我想将data指定的path的子哈希替换为update,以便我最终得到:

data == {
  a: {
    b: {
      c: {
        d: {
          g: :h
        }
      }
    }
  }
}

导航到子哈希很容易:

_data = data
path.each{|p| _data = data[p]}

但这不起作用,因为_data = update更改_data引用的内容,并且实际上并未更改data[:a][:b][:b][:d]的值。

虽然我可以编写一个导致子哈希的表达式,但尝试分配它会导致语法错误:

> (path.inject(data){|_data, s| _data[s]}) = update
SyntaxError: unexpected '=', expecting end-of-input

有什么想法吗?

3 个答案:

答案 0 :(得分:4)

您可以使用Hash#replace

> path.inject(data){|_data, s| _data[s]}.replace({g: :h})
> data
# => {:a=>{:b=>{:c=>{:d=>{:g=>:h}}}}} 

答案 1 :(得分:1)

def substitute(data, path, update)
  (data[path.first] = update; return) if path.size == 1
  substitute(data[path.first], path.drop(1), update)
end

substitute(data, path, update)
  #=> nil
data
  #=> {:a=>{:b=>{:c=>{:d=>{:g=>:h}}}}} 

要返回data(以及修改它),请添加data作为最后一行。

答案 2 :(得分:1)

构建嵌套散列(Ruby 1.8.7)的更新副本的递归解决方案。

def substitute(data, path, update)
  if path.empty? 
    update
  else
    first, *rest = path
    Hash[*data.map { |k, v| first == k ? [k, substitute(v, rest, update)] : [k ,v] }.flatten]
  end
end

result = substitute(data, path, update)
puts "In  #{data.inspect}"   # => In  {:a=>{:b=>{:c=>{:d=>{:e=>:f}}}}}
puts "Out #{result.inspect}" # => Out {:a=>{:b=>{:c=>{:d=>{:g=>:h}}}}}