我已将您的示例数组修改如下:
arr = [
{"May 21"=> [{:applications_one=> 20}, {:applications_two=> 0}]},
{"Jun 21"=> [{:applications_two=> 0}, {:applications_one=> 15}]},
{"Jul 21"=> [{:applications_one=> 6}, {:applications_three=>4}]},
{"Aug 21"=> [{:applications_one=> 1}, {:applications_two=> 2}]},
{"Sep 21"=> [{:applications_one=> 8}, {:applications_two=> 11}]},
{"Oct 21"=> [{:applications_three=>7}, {:applications_one=> 1}]}
]
我这样做的目的是展示我在下面建议的代码:
- 不需要对日期字符串的值(数组)内的哈希值进行排序;
- 不要求预先知道值内的哈希键的名称(例如,
:applications_one);和
- 允许日期字符串的值(数组)包含任意数量的哈希值。
感兴趣的值可以如下计算。
arr.each_with_object(Hash.new { |h,k| h[k] = [] }) do |g,h|
date, a = g.flatten
h[:dates] << date
a.each do |f|
label, value = f.flatten
h[label] << value
end
end
#=> {
# :dates=>["May 21", "Jun 21", "Jul 21", "Aug 21", "Sep 21", "Oct 21"],
# :applications_one=>[20, 15, 6, 1, 8, 1],
# :applications_two=>[0, 0, 2, 11],
# :applications_three=>[4, 7]
}
计算过程如下。
最初,
h = Hash.new { |h,k| h[k] = [] }
#=> {}
然后将第一个哈希传递给块(由块变量g保存)并执行块操作。
g = {"May 21"=> [{:applications_one=> 20}, {:applications_two=> 0}]}
date, a = g.flatten
#=> ["May 21", [{:applications_one=>20}, {:applications_two=>0}]]
# therefore, date #=> "May 21" and
# a #=> [{:applications_one=>20}, {:applications_two=>0}]]
见Hash#flatten。
h[:dates] << date
# now h #=> {:dates=>["May 21"]}
a.each do |f|
puts "f=#{f}"
label, value = f.flatten
puts "label=#{label}, value=#{value}"
h[label] << value
puts "h=#{h}\n"
end
显示如下:
f={:applications_one=>20}
label=applications_one, value=20
h={:dates=>["May 21"], :applications_one=>[20]}
f={:applications_two=>0}
label=applications_two, value=0
h={:dates=>["May 21"], :applications_one=>[20], :applications_two=>[0]}
arr 的下一个元素现在被传递到块并执行块计算。
g #=> {"Jun 21"=> [{:applications_two=> 0}, {:applications_one=> 15}]}
date, a = g.flatten
#=> ["Jun 21", [{:applications_two=>0}, {:applications_one=>15}]]
# therefore, date #=> "Jun 21" and
# a #=> [{:applications_two=>0}, {:applications_one=>15}]
h[:dates] << date
# now h #=> {:dates=>["May 21", "Jun 21"]}
a.each do |f|
puts "f=#{f}"
label, value = f.flatten
puts "label=#{label}, value=#{value}"
h[label] << value
puts "h=#{h}\n"
end
显示如下。
f={:applications_two=>0}
label=applications_two, value=0
h={:dates=>["May21", "Jun21"], :applications_one=>[20],
:applications_two=>[0, 0]}
f={:applications_one=>15}
label=applications_one, value=15
h={:dates=>["May21", "Jun21"], :applications_one=>[20, 15],
:applications_two=>[0, 0]}
其余的计算类似。
注意,当arr 的第二个元素被传递到块时,
h #=> {:dates=>["May 21"], :applications_one=>[20],
# :applications_two=>[0]} g[:date] #=> "Jun 21"
以下计算非常有意义:
h[:dates] << date
#=> ["May 21"] << "Jun 21"
#=> ["May 21", "Jun 21"]
相比之下,当arr的第一个元素被传递给块时,在h #=> {}时进行了以下计算,因此h[:dates] #=> nil:
h[:dates] << date
h #=> {:dates=>["May 21"]}
您可能想知道为什么会这样,因为nil 没有方法<<。这是因为h 的定义方式:
h = Hash.new { |h,k| h[k] = [] }
查看Hash::new 的形式,它接受一个块(因此没有参数)。
这意味着如果h 没有键k 并且可以通过操作更改,则首先执行分配h[k] = []。 (这不适用于例如m = h[k]; m #=> nil,因为h 没有被更改。)
处理后
{"May 21"=> [{:applications_one=> 20}, {:applications_two=> 0}]}
我们正在构建的哈希如下:
h #=> {:dates=>["May 21"], :applications_one=>[20],
# :applications_two=>[0]}
对于问题中给出的示例,返回以下内容:
{:dates=>["May 21", "Jun 21", "Jul 21", "Aug 21"],
:applications_one=>[20, 15, 8, 1],
:applications_two=>[0, 0, 11, 2]}