繁体   English   中英

将数组转换为散列并将计数器值添加到新散列中

[英]Convert array into hash and add a counter value to the new hash

我有以下哈希数组:

[
  {"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}
   }

我们得到数组:

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}} 

是我们需要的结果。 看到哈希#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 = 1count: 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

这做到了:

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/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM