我试图从哈希值中对值进行求和,并且我遇到了一些意外(对我而言)的行为。下面是重现行为的代码。
records = [
{ "id" => 5062311, "period" => "May 27, 2018", "items" => 2, "compliant_items" => 2 },
{ "id" => 5062311, "period" => "May 20, 2018", "items" => 3, "compliant_items" => 1 },
{ "id" => 5062311, "period" => "May 13, 2018", "items" => 7, "compliant_items" => 7 },
{ "id" => 5062311, "period" => "May 13, 2018", "items" => 8, "compliant_items" => 7 },
{ "id" => 5062311, "period" => "Jun 03, 2018", "items" => 6, "compliant_items" => 6 }
]
创建输出哈希
items = records.flat_map { |item| item["id"] }.uniq
weeks = records.flat_map { |item| item["period"] }.uniq
temp = items.each_with_object({}) { |item, hash| hash[item] = weeks.product(["total" => 0, "compliant" => 0]).to_h }
输出" temp"如下......
{
5062311=>{
"May 27, 2018"=>{"total"=>0, "compliant"=>0},
"May 20, 2018"=>{"total"=>0, "compliant"=>0},
"May 13, 2018"=>{"total"=>0, "compliant"=>0},
"Jun 03, 2018"=>{"total"=>0, "compliant"=>0}
}
}
现在,如果我尝试修改特定键的值,则每个其他键也会更新。例如,以下内容:
temp[5062311]["May 20, 2018"]["total"] += 5
...产量
{
5062311=>{
"May 27, 2018"=>{"total"=>5, "compliant"=>0},
"May 20, 2018"=>{"total"=>5, "compliant"=>0},
"May 13, 2018"=>{"total"=>5, "compliant"=>0},
"Jun 03, 2018"=>{"total"=>5, "compliant"=>0}
}
}
我希望只更新5月20日的记录,并且所有其他值保持为0.我不知道如何重写这个来解决这个问题。有什么建议?感谢。
答案 0 :(得分:4)
您的问题基本上如下。
a = ["dog", "cat"]
b = [:d1, :d2]
c = { :f=>1 }
h = a.each_with_object({}) { |pet, h| h[pet] = b.product([c]).to_h }
#=> {"dog"=>{:d1=>{:f=>1}, :d2=>{:f=>1}},
# "cat"=>{:d1=>{:f=>1}, :d2=>{:f=>1}}
现在让我们在其中一个哈希:f
{ :f=>1 }
的值
h["cat"][:d2][:f] = 2
然后观察h
的新值。
h #=> {"dog"=>{:d1=>{:f=>2}, :d2=>{:f=>2}},
# "cat"=>{:d1=>{:f=>2}, :d2=>{:f=>2}}}
您期望h
等于:
#=> {"dog"=>{:d1=>{:f=>1}, :d2=>{:f=>1}},
# "cat"=>{:d1=>{:f=>1}, :d2=>{:f=>2}}}
要了解我们获得此结果的原因,请将每个哈希值{ :f=>1 }
替换为object_id
。
a.each { |aa| d.each { |bb| h[aa][bb] = h[aa][bb].object_id } }
h #=> {"dog"=>{:d1=>36327481, :d2=>36327481},
# "cat"=>{:d1=>36327481, :d2=>36327481}}
如你所见,这四个哈希是同一个对象。因此,应该期望如果对象被更改,则更改将出现在哈希出现的任何位置。
以下是解决问题的一种方法。
h = a.each_with_object({}) { |pet, h| h[pet] = b.map { |d| [d, c.dup] }.to_h }
#=> {"dog"=>{:d1=>{:f=>1}, :d2=>{:f=>1}},
# "cat"=>{:d1=>{:f=>1}, :d2=>{:f=>1}}}
h["cat"][:d2][:f] = 2
#=> 2
h #=> {"dog"=>{:d1=>{:f=>1}, :d2=>{:f=>1}},
# "cat"=>{:d1=>{:f=>1}, :d2=>{:f=>2}}}
警告:如果{ :f=>1 }
包含嵌套元素,例如{ :f=>{ :g=>1 } }
,我们就不能简单地复制它(因为更改为{ :g=>1 }
会影响所有哈希值);相反,我们需要一个deep copy。