Preencha uma matriz de hash com valores ausentes
Estou tentando construir alguns gráficos de mapas de calor específicos para ApexChart . Até agora, recebi este Array de Hash, para 3 atividades.
[{
:name => "Activity 1",
:data => {
"May 2020" => 37, "June 2020" => 17, "July 2020" => 9, "August 2020" => 18
}
}, {
:name => "Activity 2",
:data => {
"May 2020" => 3
}
}, {
:name => "Activity 3",
:data => {
"July 2020" => 5, "November 2020" => 11
}
}]
Na Atividade 3, obtivemos apenas 2 meses, que são, July
e November
.
Minhas necessidades seriam preencher para cada Hash, todas as datas ausentes e preenchê-las com 0 como valor. Meus resultados esperados seriam
[{
:name => "Activity 1",
:data => {
"May 2020" => 37, "June 2020" => 17, "July 2020" => 9, "August 2020" => 18, "November 2020" => 0
}
}, {
:name => "Activity 2",
:data => {
"May 2020" => 3, "June 2020" => 0, "July 2020" => 0, "August 2020" => 0, "November 2020" => 0
}
}, {
:name => "Activity 3",
:data => {
"May 2020" => , "June 2020" => 0, "July 2020" => 5, "August 2020" => 0, "November 2020" => 11
}
}]
Sim, September
está faltando de propósito. Suponho que a melhor maneira de conseguir isso seria recuperar a cada mês, um por um; Em seguida, para preencher cada matriz com os meses ausentes; Mas não sei como fazer isso.
Respostas
Se arr
for o seu array de hashes, você pode construir o array desejado em duas etapas.
require 'date'
date_fmt = "%B %Y"
first_month, last_month = arr.flat_map do |g|
g[:data].keys
end.map { |s| Date.strptime(s, date_fmt) }.minmax
#=> [#<Date: 2020-05-01 ((2458971j,0s,0n),+0s,2299161j)>,
# #<Date: 2020-11-01 ((2459155j,0s,0n),+0s,2299161j)>]
h = (first_month..last_month).map do |d|
d.strftime(date_fmt)
end.product([0]).to_h
#=> {"May 2020"=>0, "June 2020"=>0, "July 2020"=>0, "August 2020"=>0,
# "September 2020"=>0, "October 2020"=>0, "November 2020"=>0}
arr.map { |g| g.merge(:data => h.merge(g[:data])) }
#=> [
# {
# :name=>"Activity 1",
# :data=>{
# "May 2020"=>37, "June 2020"=>17, "July 2020"=>9,
# "August 2020"=>18, "September 2020"=>0,
# "October 2020"=>0, "November 2020"=>0
# }
# },
# {
# :name=>"Activity 2",
# :data=>{
# "May 2020"=>3, "June 2020"=>0, "July 2020"=>0,
# "August 2020"=>0, "September 2020"=>0,
# "October 2020"=>0, "November 2020"=>0
# }
# },
# {
# :name=>"Activity 3",
# :data=>{
# "May 2020"=>0, "June 2020"=>0, "July 2020"=>5,
# "August 2020"=>0, "September 2020"=>0,
# "October 2020"=>0, "November 2020"=>11
# }
# }
# ]
Consulte Enumerable # flat_map , Date :: strptime , Array # minmax , Date # strftime , Array # product e Hash # merge . Consulte também DateTime # strptime para obter as diretivas de formatação de data.
Observe que no cálculo de first_month
e last_month
,
[#<Date: 2020-05-01 ((2458971j,0s,0n),+0s,2299161j)>,
#<Date: 2020-11-01 ((2459155j,0s,0n),+0s,2299161j)>].
map { |d| d.strftime(date_fmt) }
#=> ["May 2020", "November 2020"]
Você pode conseguir isso por algo semelhante, primeiro buscando os meses e, em seguida, modificando os itens da matriz que são Hashes no seu caso:
months = data.flat_map { |d| d[:data].keys }.to_set
data.each do |d|
months.each do |month|
d[:data][month] = 0 unless d[:data].key?(month)
end
end
O que você também pode fazer é criar um novo em Array
que os Hash
valores sejam inicializados com um valor padrão:
data_with_default = data.map do |d|
{
name: d[:name],
data: Hash.new(0).update(d[:data])
}
end
Experimente o seguinte:
a = [{
:name => "Activity 1",
:data => {
"May 2020" => 37, "June 2020" => 17, "July 2020" => 9, "August 2020" => 18
}
}, {
:name => "Activity 2",
:data => {
"May 2020" => 3
}
}, {
:name => "Activity 3",
:data => {
"July 2020" => 5, "November 2020" => 11
}
}]
months = ['May 2020', 'June 2020', 'July 2020', 'August 2020', 'November 2020']
puts a.map { |h| { name: h[:name], data: Hash[months.map { |m| [m, h[:data][m] ? h[:data][m] : 0] }] } }