我有以下哈希数组:
[
{"BREAD" => {:price => 1.50, :discount => true }},
{"BREAD" => {:price => 1.50, :discount => true }},
{"MARMITE" => {:price => 1.60, :discount => false}}
]
我想将此数组转换为包含每个项目计数的哈希:
输出:
{
"BREAD" => {:price => 1.50, :discount => true, :count => 2},
"MARMITE" => {:price => 1.60, :discount => false, :count => 1}
}
我尝试了两种方法将数组转换为哈希。
new_cart = cart.inject(:merge)
hash = Hash[cart.collect { |item| [item, ""] } ]
两者都可以,但是后来我对如何捕获和传递计数值感到困惑。
预期产量
{
"BREAD" => {:price => 1.50, :discount => true, :count => 2},
"MARMITE" => {:price => 1.60, :discount => false, :count => 1}
}
答案 0 :(得分:0)
我们得到了数组:
arr = [
{"BREAD" => {:price => 1.50, :discount => true }},
{"BREAD" => {:price => 1.50, :discount => true }},
{"MARMITE" => {:price => 1.60, :discount => false}}
]
并假设每个哈希具有一个密钥,并且如果两个哈希具有相同的(单个)密钥,则在两个哈希中该密钥的值都相同。
第一步是创建一个空哈希,将在其中添加键值对:
h = {}
现在,我们遍历arr
来构建哈希h
。我添加了一个puts
语句以在计算中显示中间值。
arr.each do |g|
k, v = g.first
puts "k=#{k}, v=#{v}"
if h.key?(k)
h[k][:count] += 1
else
h[k] = v.merge({ :count => 1 })
end
end
显示:
k=BREAD, v={:price=>1.5, :discount=>true}
k=BREAD, v={:price=>1.5, :discount=>true}
k=MARMITE, v={:price=>1.6, :discount=>false}
并返回:
#=> [{"BREAD" =>{:price=>1.5, :discount=>true}},
# {"BREAD" =>{:price=>1.5, :discount=>true}},
# {"MARMITE"=>{:price=>1.6, :discount=>false}}]
each
总是返回其接收者(这里是arr
),这不是我们想要的。
h #=> {"BREAD"=>{:price=>1.5, :discount=>true, :count=>2},
# "MARMITE"=>{:price=>1.6, :discount=>false, :count=>1}}
是我们需要的结果。请参见Hash#key?(又名has_key?
),Hash#[],Hash#[]=和Hash#merge。
现在让我们将其包装在一个方法中。
def hashify(arr)
h = {}
arr.each do |g|
k, v = g.first
if h.key?(k)
h[k][:count] += 1
else
h[k] = v.merge({ :count=>1 })
end
end
h
end
hashify(arr)
#=> {"BREAD"=>{:price=>1.5, :discount=>true, :count=>2},
# "MARMITE"=>{:price=>1.6, :discount=>false, :count=>1}}
Rubyists通常会使用方法Enumerable#each_with_object进行简化。
def hashify(arr)
arr.each_with_object({}) do |g,h|
k, v = g.first
if h.key?(k)
h[k][:count] += 1
else
h[k] = v.merge({ :count => 1 })
end
end
end
比较两种方法以识别它们的差异。参见Enumerable#each_with_object。
当键(如此处)是符号时,Ruby允许您将简写{ count: 1 }
用作{ :count=>1 }
。此外,当散列为参数时,她允许您写:count = 1
或count: 1
时不用大括号。例如,
{}.merge('cat'=>'meow', dog:'woof', :pig=>'oink')
#=> {"cat"=>"meow", :dog=>"woof", :pig=>"oink"}
当键是符号时,看到count: 1
的形式可能更常见,而当散列是参数时,省略大括号。
您可能会在这里看到进一步的改进。首先创建
h = arr.group_by { |h| h.keys.first }
#=> {"BREAD" =>[{"BREAD"=>{:price=>1.5, :discount=>true}},
# {"BREAD"=>{:price=>1.5, :discount=>true}}],
# "MARMITE"=>[{"MARMITE"=>{:price=>1.6, :discount=>false}}]}
请参见Enumerable#group_by。现在将值(数组)转换为其大小:
counts = h.transform_values { |arr| arr.size }
#=> {"BREAD"=>2, "MARMITE"=>1}
可以缩写形式:
counts = h.transform_values(&:size)
#=> {"BREAD"=>2, "MARMITE"=>1}
请参见Hash#transform_values。我们现在可以写:
uniq_arr = arr.uniq
#=> [{"BREAD"=>{:price=>1.5, :discount=>true}},
#= {"MARMITE"=>{:price=>1.6, :discount=>false}}]
uniq_arr.each_with_object({}) do |g,h|
puts "g=#{g}"
k,v = g.first
puts " k=#{k}, v=#{v}"
h[k] = v.merge(counts: counts[k])
puts " h=#{h}"
end
其中显示:
g={"BREAD"=>{:price=>1.5, :discount=>true}}
k=BREAD, v={:price=>1.5, :discount=>true}
h={"BREAD"=>{:price=>1.5, :discount=>true, :counts=>2}}
g={"MARMITE"=>{:price=>1.6, :discount=>false}}
k=MARMITE, v={:price=>1.6, :discount=>false}
h={"BREAD"=>{:price=>1.5, :discount=>true, :counts=>2},
"MARMITE"=>{:price=>1.6, :discount=>false, :counts=>1}}
并返回:
#=> {"BREAD"=>{:price=>1.5, :discount=>true, :counts=>2},
# "MARMITE"=>{:price=>1.6, :discount=>false, :counts=>1}}
请参见Array#uniq。
答案 1 :(得分:0)
这可以解决问题:
arr = [
{ bread: { price: 1.50, discount: true } },
{ bread: { price: 1.50, discount: true } },
{ marmite: { price: 1.60, discount: false } }
]
获取每次哈希值的计数,添加为键值对并存储:
h = arr.uniq.each { |x| x[x.first.first][:count] = arr.count(x) }
然后将散列转换为数组,展平为单个数组,然后构造一个哈希:
Hash[*h.collect(&:to_a).flatten]
#=> {:bread=>{:price=>1.50, :discount=>true, :count=>2}, :marmite=>{:price=>1.60, :discount=>false, :count=>1}}
结合了以下两个不错的主意: https://raycodingdotnet.wordpress.com/2013/08/05/array-of-hashes-into-single-hash-in-ruby/ 和这里: http://carol-nichols.com/2015/08/07/ruby-occurrence-couting/