Ruby on Rails - Ruby,如何添加来自具有相同键的两个散列的值,而不覆盖值?
Ruby on Rails - Ruby, How to add the values from two hashes with same key, without overwriting the values?
首先感谢您在这一点上帮助我解决 SQL 问题。
现在我正在为另一件事而苦恼,这让我思考是否应该老实说放弃成为一名程序员。
无论如何,我的问题是:我有一个散列数组(在该散列中还有另一个),如下所示:
[
{"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}},
{"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}},
{"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}},
...
]
我想要完成的是,如果有两个值具有相同的键,即“A1”,将这些值添加到一个完整的哈希中,而不覆盖旧值并将月份作为所需的键输出:
[
{"A1"=> { 1 => { :balance=> "0.0000", :price=>"9.0000"} },
{ 7 => { :balance => "34030.0000", :price => "34030.0000" } }},
and so on...
]
这可能吗?
由于您所拥有的数据的当前格式,您将需要进行不止几次转换。他们中的大多数基于转换结果散列的值,在通过它们的唯一键对数组中的散列进行分组之后:
data
.group_by { |hash| hash.keys.first } # (1)
.transform_values { |value| value.flat_map(&:values) } # (2)
.transform_values { |values| values.index_by { |value| value[:month] } } # (3)
第一个转换是根据其唯一的散列键对当前对象进行分组,该对象包含一个散列数组,因此 keys.first
,结果为:
{
"A1"=>[
{"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}},
{"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}}
],
"A3"=>[{"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}]
}
第二种是仅从每个散列中提取值,在生成的散列中,使用散列数组:
{
"A1"=>[
{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"},
{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}
],
"A3"=>[{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}]
}
然后,它只是缺乏将哈希数组转换为简单的哈希,其键是 month
:
的值
{
"A1"=>{
1.0=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"},
7.0=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}
},
"A3"=>{4.0=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}
}
@Sebastian 的回答非常好。对于多样性,我们还考虑一种迭代方法。不知道这样更高效还是更容易理解,但是多角度理解总是好的。
正在设置您提供给我们的输入数据:
arr = [
{"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}},
{"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}},
{"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}]
我们为我们的结果创建一个新的空哈希。
new_hash = {}
现在迭代原始数据。我们将对数据的形式做出一些假设。
# We know each thing is going to be a hash.
arr.each do |hsh|
# Set up some convenient variables for keys and
# values we'll need later.
key = hsh.keys.first
value = hsh.values.first
month = value[:month]
# If the output hash doesn't yet have the key,
# give it the key and assign an empty hash to it.
new_hash[key] ||= {}
# Assign the value to the hash, keyed to the current month.
new_hash[key][month] = value
# ... and get rid of the month key that's now redundant.
new_hash[key][month].delete(:month)
end
结果是:
{"A1"=>{1.0=>{:balance=>"0.0000", :price=>"9.0000"},
7.0=>{:balance=>"34030.0000", :price=>"34030.0000"}},
"A3"=>{4.0=>{:balance=>"34030.0000", :price=>"34030.0000"}}}
可以说,将所需的 return 值作为散列会更有用:
h = {"A1"=>{1=>{:balance=> "0.0000", :price=> "9.0000"},
7=>{:balance=>"34030.0000", :price=>"34030.0000"}},
"A3"=>{4=>{:balance=>"34030.0000", :price=>"34030.0000"}}}
这样你可以这样写,例如:
require 'bigdecimal'
BigDecimal(h['A1'][7][:price])
#=> 0.3403e5
参见 BigDecimal。 BigDecimal
一般用于财务计算,因为它可以避免舍入误差。
这个结果可以通过将:month
的值改为arr
中的整数得到:
arr = [
{"A1"=>{:month=>1, :balance=> "0.0000", :price=> "9.0000"}},
{"A1"=>{:month=>7, :balance=>"34030.0000", :price=>"34030.0000"}},
{"A3"=>{:month=>4, :balance=>"34030.0000", :price=>"34030.0000"}}
]
并通过计算:
h = arr.each_with_object({}) do |g,h|
k,v = g.flatten
(h[k] ||= {}).update(v[:month]=>v.reject { |k,_| k == :month })
end
参见Hash#flatten, Hash#update (aka merge!
) and Hash#reject。
也可以这样写:
h = arr.each_with_object(Hash.new { |h,k| h[k] = {} }) do |g,h|
k,v = g.flatten
h[k].update(v[:month]=>v.reject { |k,_| k == :month })
end
见 Hash::new 占块的形式
首先感谢您在这一点上帮助我解决 SQL 问题。
现在我正在为另一件事而苦恼,这让我思考是否应该老实说放弃成为一名程序员。
无论如何,我的问题是:我有一个散列数组(在该散列中还有另一个),如下所示:
[
{"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}},
{"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}},
{"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}},
...
]
我想要完成的是,如果有两个值具有相同的键,即“A1”,将这些值添加到一个完整的哈希中,而不覆盖旧值并将月份作为所需的键输出:
[
{"A1"=> { 1 => { :balance=> "0.0000", :price=>"9.0000"} },
{ 7 => { :balance => "34030.0000", :price => "34030.0000" } }},
and so on...
]
这可能吗?
由于您所拥有的数据的当前格式,您将需要进行不止几次转换。他们中的大多数基于转换结果散列的值,在通过它们的唯一键对数组中的散列进行分组之后:
data
.group_by { |hash| hash.keys.first } # (1)
.transform_values { |value| value.flat_map(&:values) } # (2)
.transform_values { |values| values.index_by { |value| value[:month] } } # (3)
第一个转换是根据其唯一的散列键对当前对象进行分组,该对象包含一个散列数组,因此 keys.first
,结果为:
{
"A1"=>[
{"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}},
{"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}}
],
"A3"=>[{"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}]
}
第二种是仅从每个散列中提取值,在生成的散列中,使用散列数组:
{
"A1"=>[
{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"},
{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}
],
"A3"=>[{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}]
}
然后,它只是缺乏将哈希数组转换为简单的哈希,其键是 month
:
{
"A1"=>{
1.0=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"},
7.0=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}
},
"A3"=>{4.0=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}
}
@Sebastian 的回答非常好。对于多样性,我们还考虑一种迭代方法。不知道这样更高效还是更容易理解,但是多角度理解总是好的。
正在设置您提供给我们的输入数据:
arr = [
{"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}},
{"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}},
{"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}]
我们为我们的结果创建一个新的空哈希。
new_hash = {}
现在迭代原始数据。我们将对数据的形式做出一些假设。
# We know each thing is going to be a hash.
arr.each do |hsh|
# Set up some convenient variables for keys and
# values we'll need later.
key = hsh.keys.first
value = hsh.values.first
month = value[:month]
# If the output hash doesn't yet have the key,
# give it the key and assign an empty hash to it.
new_hash[key] ||= {}
# Assign the value to the hash, keyed to the current month.
new_hash[key][month] = value
# ... and get rid of the month key that's now redundant.
new_hash[key][month].delete(:month)
end
结果是:
{"A1"=>{1.0=>{:balance=>"0.0000", :price=>"9.0000"},
7.0=>{:balance=>"34030.0000", :price=>"34030.0000"}},
"A3"=>{4.0=>{:balance=>"34030.0000", :price=>"34030.0000"}}}
可以说,将所需的 return 值作为散列会更有用:
h = {"A1"=>{1=>{:balance=> "0.0000", :price=> "9.0000"},
7=>{:balance=>"34030.0000", :price=>"34030.0000"}},
"A3"=>{4=>{:balance=>"34030.0000", :price=>"34030.0000"}}}
这样你可以这样写,例如:
require 'bigdecimal'
BigDecimal(h['A1'][7][:price])
#=> 0.3403e5
参见 BigDecimal。 BigDecimal
一般用于财务计算,因为它可以避免舍入误差。
这个结果可以通过将:month
的值改为arr
中的整数得到:
arr = [
{"A1"=>{:month=>1, :balance=> "0.0000", :price=> "9.0000"}},
{"A1"=>{:month=>7, :balance=>"34030.0000", :price=>"34030.0000"}},
{"A3"=>{:month=>4, :balance=>"34030.0000", :price=>"34030.0000"}}
]
并通过计算:
h = arr.each_with_object({}) do |g,h|
k,v = g.flatten
(h[k] ||= {}).update(v[:month]=>v.reject { |k,_| k == :month })
end
参见Hash#flatten, Hash#update (aka merge!
) and Hash#reject。
也可以这样写:
h = arr.each_with_object(Hash.new { |h,k| h[k] = {} }) do |g,h|
k,v = g.flatten
h[k].update(v[:month]=>v.reject { |k,_| k == :month })
end
见 Hash::new 占块的形式