将 2 个散列与相同的键结合起来

Combine 2 hashes with the same key

我想合并 2 个具有相同键的哈希值。

@clean_by_hour = Sale.where(item_name: clean).group_by_hour_of_day(:complete_time, format: "%-l %P").count
=> {"12 am"=>0, "1 am"=>0, "2 am"=>0, "3 am"=>0, "4 am"=>0, "5 am"=>0, "6 am"=>0, "7 am"=>0, "8 am"=>4, "9 am"=>14, "10 am"=>19, "11 am"=>10, "12 pm"=>19, "1 pm"=>16, "2 pm"=>13, "3 pm"=>18, "4 pm"=>7, "5 pm"=>4, "6 pm"=>4, "7 pm"=>0, "8 pm"=>0, "9 pm"=>0, "10 pm"=>0, "11 pm"=>0}

@lube_by_hour = Sale.where(item_name: lube).group_by_hour_of_day(:complete_time, format: "%-l %P").count

=> {"12 am"=>0, "1 am"=>0, "2 am"=>0, "3 am"=>0, "4 am"=>0, "5 am"=>0, "6 am"=>0, "7 am"=>0, "8 am"=>3, "9 am"=>4, "10 am"=>10, "11 am"=>14, "12 pm"=>10, "1 pm"=>8, "2 pm"=>5, "3 pm"=>20, "4 pm"=>4, "5 pm"=>2, "6 pm"=>0, "7 pm"=>0, "8 pm"=>0, "9 pm"=>0, "10 pm"=>0, "11 pm"=>0}

我希望新哈希看起来像:

{"12 am"=> 0, 0}

至少那是我认为我想要的。我正在尝试合并这两个哈希值,以便可以在 table.

中显示数据

我知道我需要更改此代码,但这就是我现在在视图中使用的内容。

    <% @clean_by_hour.each do |hour, count| %>
    
    <% if count != 0 %>
      <tr>
        <td><%= hour %></td>
        <td><%= count %></td>
      <% end %>
    <% end %>
    <% @lube_by_hour.each do |hour, count| %>
    <% if count != 0 %>
      <td><%= count %></td>
     <% end %>
    <% end %>
      </tr>

谢谢!

您可以为此使用 #each_with_object。不确定这是否是最好的方法,但它可以满足您的需求。

@clean_by_hour.each_with_object({}) do |(k,v), acc|
  acc[k] ||= []
  acc[k] << v
  acc[k] << @lube_by_hour[k]
  acc[k].compact
end

将return{'12 am' => [0,0], '1 am' => [0,0], ... }

首先,我认为 {"12 am"=> 0, 0} 是指一个哈希,其每个键都是一个小时,每个值都是一个包含两个元素的数组,一个代表该小时内的清洁次数,另一个代表该小时内的清洁次数润滑油。如果是这种情况,它应该是这样的:{"12 am"=> [0, 0]}(每个值周围有 []

你可以用这样的方法来做到这一点:

@count_by_hour = @clean_by_hour.keys.each_with_object({}) do |k, h|
  h[k] = [@clean_by_hour[k], @lube_by_hour[k]]
end

@count_by_hour # =>
{"12 am"=>[0, 0],
 "1 am"=>[0, 0],
 "2 am"=>[0, 0],
 "3 am"=>[0, 0],
 "4 am"=>[0, 0],
 "5 am"=>[0, 0],
 "6 am"=>[0, 0],
 "7 am"=>[0, 0],
 "8 am"=>[4, 3],
 "9 am"=>[14, 4],
 "10 am"=>[19, 10],
 "11 am"=>[10, 14],
 "12 pm"=>[19, 10],
 "1 pm"=>[16, 8],
 "2 pm"=>[13, 5],
 "3 pm"=>[18, 20],
 "4 pm"=>[7, 4],
 "5 pm"=>[4, 2],
 "6 pm"=>[4, 0],
 "7 pm"=>[0, 0],
 "8 pm"=>[0, 0],
 "9 pm"=>[0, 0],
 "10 pm"=>[0, 0],
 "11 pm"=>[0, 0]}

您还可以为每个值使用哈希来提高可读性并防止错误:(记住使用哪个哈希键比记住哪个数组索引更容易。)

@count_by_hour = @clean_by_hour.keys.each_with_object({}) do |k, h|
  h[k] = { clean: @clean_by_hour[k], lube: @lube_by_hour[k] }
end

@count_by_hour # =>
{"12 am"=>{:clean=>0, :lube=>0},
 "1 am"=>{:clean=>0, :lube=>0},
 "2 am"=>{:clean=>0, :lube=>0},
 "3 am"=>{:clean=>0, :lube=>0},
 "4 am"=>{:clean=>0, :lube=>0},
 "5 am"=>{:clean=>0, :lube=>0},
 "6 am"=>{:clean=>0, :lube=>0},
 "7 am"=>{:clean=>0, :lube=>0},
 "8 am"=>{:clean=>4, :lube=>3},
 "9 am"=>{:clean=>14, :lube=>4},
 "10 am"=>{:clean=>19, :lube=>10},
 "11 am"=>{:clean=>10, :lube=>14},
 "12 pm"=>{:clean=>19, :lube=>10},
 "1 pm"=>{:clean=>16, :lube=>8},
 "2 pm"=>{:clean=>13, :lube=>5},
 "3 pm"=>{:clean=>18, :lube=>20},
 "4 pm"=>{:clean=>7, :lube=>4},
 "5 pm"=>{:clean=>4, :lube=>2},
 "6 pm"=>{:clean=>4, :lube=>0},
 "7 pm"=>{:clean=>0, :lube=>0},
 "8 pm"=>{:clean=>0, :lube=>0},
 "9 pm"=>{:clean=>0, :lube=>0},
 "10 pm"=>{:clean=>0, :lube=>0},
 "11 pm"=>{:clean=>0, :lube=>0}}

让我们从简化您的示例开始:

clean_by_hour = { "12 am"=>0, "1 am"=>3, "2 am"=>0, "3 am"=>5 }
lube_by_hour  = { "12 am"=>0, "1 am"=>4, "2 am"=>0, "3 am"=>2 }

据推测,table 具有来自每个按小时排序(按键插入时间)的哈希的键值对,因此您希望计算以下内容:

hours = clean_by_hour.keys
  #=> ["12 am", "1 am", "2 am", "3 am"]
clean_values = clean_by_hour.values
  #=> [0, 3, 0, 5]
lube_values = lube_by_hour.values
  #=> [0, 4, 0, 2]
arr = clean_values.zip(lube_values)
  #=> [[0, 0], [3, 4], [0, 0], [5, 2]]

然后使用 hoursarr 构造您的 table.


但是,这要求每个散列的键的插入顺序都按照显示的顺序。写成下面这样比较稳妥

hours = ["12 am", "1 am", "2 am", "3 am"]
clean_values = clean_by_hour.values_at(*hours)
  #=> [0, 3, 0, 5]
lube_values = lube_by_hour.values_at(*hours)
  #=> [0, 4, 0, 2]
arr = clean_values.zip(lube_values)
  #=> [[0, 0], [3, 4], [0, 0], [5, 2]]

无论散列中键值对的顺序如何,这都有效。参见 Hash#values_at


退一步说,您可能会考虑使用 24 小时制以不同方式构建原始哈希。例如:

clean_by_hour = { 0=>0, 1=>3, 12=>5, 13=>1, 23=>6 }
lube_by_hour  = { 0=>0, 1=>4, 12=>3, 13=>2, 23=>3 }

然后

hours = [0, 1, 12, 13, 23]
clean_values = clean_by_hour.values_at(*hours)
  #=> [0, 3, 5, 1, 6]
lube_values = lube_by_hour.values_at(*hours)
  #=> [0, 4, 3, 2, 3]
arr = clean_values.zip(lube_values)
  #=> [[0, 0], [3, 4], [5, 3], [1, 2], [6, 3]]

如果您希望像示例中那样在 table 中表达小时数,您可以使用一种简单的方法:

def hours_to_str(hour)
  case hour
  when 0 then "12 am"
  when 1..11 then "#{hour} am"
  when 12 then "12 pm"
  else "#{hour-12} pm"
  end
end
hours_to_str(0)  #=> "12 am"
hours_to_str(3)  #=> "3 am"
hours_to_str(11) #=> "11" am"
hours_to_str(12) #=> "12 pm"
hours_to_str(13) #=> "1 pm"       
hours_to_str(23) #=> "11 pm"

然后使用

am_pm_hours = hours.map { |hour| hour_to_str(hour) }
  #=> ["12 am", "1 am", "12 pm", "1 pm", "11 pm"]

构建你的table.

假设您稍后决定 am_pm_hours 为:

["12:00 am", "1:00 am", "12:00 pm", "1:00 pm", "11:00 pm"]

那你只需要改变方法hours_to_str,而不是哈希clean_by_hourlube_by_hour.

的构造

最后,由于您正在获取一天中所有 24 小时的值,因此您可以按顺序创建数组而不是散列(我的偏好)。

clean_by_hour = [0, 3, 5, 3, 1, 6, 2, 7, 1, 2, 3, 4,
                 5, 1, 8, 2, 6, 9, 1, 5, 8, 9, 3, 6]
lube_by_hour  = [0, 4, 6, 3, 1, 4, 2, 3, 4, 4, 1, 0,
                 3, 2, 4, 1, 4, 2, 7, 5, 3, 6, 9, 3]

Hash#merge

这个问题已经有很多很好的答案,但我有点奇怪,还没有人提到 Ruby 的块形式的内置 Hash#merge

请看下面的例子:

clean_by_hour = { "12 am" => 0, "1 am" => 4, "2 am" => 21 }
lube_by_hour = { "12 am" => 0, "1 am" => 17, "2 am" => 12 }

clean_by_hour.merge(lube_by_hour) { |_key, old_value, new_value| [old_value, new_value] }
# => {"12 am"=>[0, 0], "1 am"=>[4, 17], "2 am"=>[21, 12]}

clean_by_hour.merge(lube_by_hour) do |_key, clean, lube|
  { clean: clean, lube: lube }
end
# {"12 am"=>{:clean=>0, :lube=>0}, "1 am"=>{:clean=>4, :lube=>17}, "2 am"=>{:clean=>21, :lube=>12}}

来源