使用默认哈希的结构创建哈希的哈希

Create hash-of-hashes using structure of the default hash

我有以下代码:

default = {:id => 0, :detail =>{:name=>"Default", :id => ""}}
employees = {}

nr = (0..3).to_a 
nr.each do |n|
    employee = default
    employee[:id] = n
    employee[:detail][:name] = "Default #{n}"
    employee[:detail][:id] = "KEY-#{n}"
    employees[n] = employee
end
puts employees

我希望 :detail 散列中的键 :id 的值为 KEY-0KEY-1KEY-2.

您在每次迭代中都制作了一个浅拷贝,即每次每个副本都被上次迭代中计算的值覆盖。您可以尝试使用以下哈希内哈希默认模式进行深度复制:

employee = Marshal.load(Marshal.dump(default))

Demonstration

您需要整理默认值才能复制

default = {id: 0, detail: {name: "Default", id:""}}
employees = {}
4.times do |n|
  employees[n] = Marshal.load(Marshal.dump(default))
  employees[n][:id] = n
  employees[n][:detail][:name] = "Default #{n}"
  employees[n][:detail][:id] = "KEY-#{n}"
end
puts employees

输出是

{0=>{:id=>0, :detail=>{:name=>"Default 0", :id=>"KEY-0"}}, 1=>{:id=>1, :detail=>{:name=>"Default 1", :id=>"KEY-1"}}, 2=>{:id=>2, :detail=>{:name=>"Default 2", :id=>"KEY-2"}}, 3=>{:id=>3, :detail=>{:name=>"Default 3", :id=>"KEY-3"}}}

你可以阅读这个post

已添加

这里有一个缩减版本,如果需要的话应该会更快。

employees = {}
4.times { |n| employees[n]={id: n, detail: {name: "Default #{n}", id:"KEY-#{n}"}} }
puts employees

您只需更改:

default = { :id=>0, :detail=>{ :name=>"Default", :id=>"" } }

def default
  {}.merge(:id=>0, :detail=>({}.merge(:name=>"Default", :id=>"")))
end

但是,嘿,当我们这样做的时候,我们也可以 Ruby-ize 其余的:

employees = (0..3).map do |n|
    employee = default
    employee[:id] = n
    employee[:detail][:name] = "Default #{n}"
    employee[:detail][:id] = "KEY-#{n}"
    employee
end
  #=> [{:id=>0, :detail=>{:name=>"Default 0", :id=>"KEY-0"}},
  #    {:id=>1, :detail=>{:name=>"Default 1", :id=>"KEY-1"}},
  #    {:id=>2, :detail=>{:name=>"Default 2", :id=>"KEY-2"}},
  #    {:id=>3, :detail=>{:name=>"Default 3", :id=>"KEY-3"}}] 

让我们确认我们正在制作 default 的深层副本:

employees[0][:detail][:id] = "cat"
employees
  #=> [{:id=>0, :detail=>{:name=>"Default 0", :id=>"cat"}},
  #    {:id=>1, :detail=>{:name=>"Default 1", :id=>"KEY-1"}},
  #    {:id=>2, :detail=>{:name=>"Default 2", :id=>"KEY-2"}},
  #    {:id=>3, :detail=>{:name=>"Default 3", :id=>"KEY-3"}}] 

你更常看到这样写的:

employees = (0..3).map do |n|
  default.merge(:id=>n, :detail=>{:name=>"Default #{n}", :id=>"KEY-#{n}"})
end
  #=> [{:id=>0, :detail=>{:name=>"Default 0", :id=>"cat"}},
  #    {:id=>1, :detail=>{:name=>"Default 1", :id=>"KEY-1"}},
  #    {:id=>2, :detail=>{:name=>"Default 2", :id=>"KEY-2"}},
  #    {:id=>3, :detail=>{:name=>"Default 3", :id=>"KEY-3"}}] 

根据其他答案的建议,您可以这样做:

class Object
  def deep_copy
    Marshal.load(Marshal.dump(self))
  end
end

那么你可以这样写:

default = { :id=>0, :detail=>{ :name=>"Default", :id=>"" } }
employees = (0..3).map do |n|
  default.deep_copy.merge(:id=>n, :detail=>{:name=>"Default #{n}",
    :id=>"KEY-#{n}"})
end
  #=> [{:id=>0, :detail=>{:name=>"Default 0", :id=>"KEY-0"}},
  #    {:id=>1, :detail=>{:name=>"Default 1", :id=>"KEY-1"}},
  #    {:id=>2, :detail=>{:name=>"Default 2", :id=>"KEY-2"}},
  #    {:id=>3, :detail=>{:name=>"Default 3", :id=>"KEY-3"}}] 

这样做的好处是,如果您更改 default,则无需进行其他更改。