我有一个Month-> Value
的哈希值{"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9}
我知道在第一个和最后一个值之间获得所有空月的最佳方法是什么
我想要像
这样的东西{"Apr 2016"=>6, May 2016=>0, Jun 2016=>0 .... "Aug 2016"=>9, "Sep 2016" => 0 "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9}
答案 0 :(得分:4)
这是另一种方式,使用each_with_object
:
def add_months(dates)
min, max = dates.keys.map { |date| Date.parse(date) }.minmax
range = (min..max).map { |date| date.strftime("%b %Y") }.uniq
range.each_with_object({}) { |date, result| result[date] = dates[date] || 0 }
end
输出:
dates = {"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9}
add_months(dates)
#=> {
# "Apr 2016"=>6,
# "May 2016"=>0,
# "Jun 2016"=>0,
# "Jul 2016"=>0,
# "Aug 2016"=>9,
# "Sep 2016"=>0,
# "Oct 2016"=>0,
# "Nov 2016"=>0,
# "Dec 2016"=>0,
# "Jan 2017"=>11,
# "Feb 2017"=>0,
# "Mar 2017"=>0,
# "Apr 2017"=>6,
# "May 2017"=>9,
# "Jun 2017"=>1,
# "Jul 2017"=>9
# }
答案 1 :(得分:3)
<强>代码强>
require 'date'
def fill_in_missing_months(dates)
date_fmt = '%b %Y'
fm, lm = dates.keys.
minmax_by { |date| Date.strptime(date, date_fmt) }.
map { |date| Date.strptime(date, date_fmt) }
(0..12*(lm.year-fm.year) + lm.month-fm.month).each_with_object({}) do |_,h|
str = fm.strftime(date_fmt)
h[str] = dates.fetch(str, 0)
fm >>= 1
end
end
示例强>
dates = {"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6,
"May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9}
fill_in_missing_months(dates)
#=> {"Apr 2016"=>6, "May 2016"=>0, "Jun 2016"=>0, "Jul 2016"=>0, "Aug 2016"=>9,
# "Sep 2016"=>0, "Oct 2016"=>0, "Nov 2016"=>0, "Dec 2016"=>0, "Jan 2017"=>11,
# "Feb 2017"=>0, "Mar 2017"=>0, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1,
# "Jul 2017"=>9}
<强>解释强>
经验丰富的Rubiests:已发布 gory-detail-follow 顾问,因此您可能希望跳过我的其余答案。
Ruby在解析代码时将fm >>= 1
扩展为fm = fm >> 1
。 Date#>>将日期推进其参数给出的月数,此处为1
。
除了:>>
之外,请参阅方法的文档Integer#times,Hash#fetch,Date::strptime,Date#strftime,Date#year,{{ 3}},Date#month和Enumerator#with_object(不包括更常用的方法,例如Enumerable#map
)。在#
中重新调用Date#year
表示实例方法,而::
中的[Date::strptime]
表示类方法。
对于示例中给出的dates
,步骤如下。
date_fmt = '%b %Y'
b = dates.keys
#=> ["Apr 2016", "Aug 2016", "Jan 2017", "Apr 2017", "May 2017",
# "Jun 2017", "Jul 2017"]
c = b.minmax_by { |date| Date.strptime(date, date_fmt) }
#=> ["Apr 2016", "Jul 2017"]
fm, lm = c.map { |date| Date.strptime(date, date_fmt) }
#=> [#<Date: 2016-04-01 ((2457480j,0s,0n),+0s,2299161j)>,
# #<Date: 2017-07-01 ((2457936j,0s,0n),+0s,2299161j)>]
fm #=> #<Date: 2016-04-01 ((2457480j,0s,0n),+0s,2299161j)>
lm #=> #<Date: 2017-07-01 ((2457936j,0s,0n),+0s,2299161j)>]
d = 0..12*(lm.year-fm.year) + lm.month-fm.month
#=> 0..15
e = d.each_with_object({})
#=> #<Enumerator: 0..15:each_with_object({})>
我们可以使用Enumerable#minimax_by(或Enumerable#entries)查看由e
生成并通过将其转换为数组传递给块的值。
e.entries
#=> [[0, {}], [1, {}],..., [15, {}]]
这些元组中的哈希最初是空的,但是会在执行块计算时填充。
第一个元素由e
生成,使用名为消歧或分解<的过程将其传递给块并将块变量设置为等于其值/ em>去除与每个块变量关联的部分。
_,h = e.next
#=> [0, {}]
h #=> {}
我已使用_
作为第一个块变量来表示它(块索引)未在块计算中使用。接着,
str = fm.strftime(date_fmt)
#=> "Apr 2016"
h[str] = dates.fetch(str, 0)
#=> 6
h #=> {"Apr 2016"=>6}
在这种情况下,dates
有一个键"Apr 2016"
,因此h["Apr 2016"]
设置为dates["Apr 2016"]
。在其他情况下,dates
的密钥不会等于str
(例如"May 2016"
),因此该值将设置为fetch
的默认值0
。
fm >>= 1
#=> #<Date: 2016-05-01 ((2457510j,0s,0n),+0s,2299161j)>
fm
现在是2016年5月。其余的计算方法类似。
答案 2 :(得分:1)
您可以使用以下方法执行上述操作:
def normalize_months(month_values)
ordered_months = month_values.keys.map do |m| Date.parse(m) end.sort.map do |s| s.strftime('%b %Y') end
normalized = []
current = ordered_months.first
while current != ordered_months.last do
normalized << current
current = Date.parse(current).next_month.strftime('%b %Y')
end
result = {}
normalized.each do |g| result[g] = month_values[g].nil? ? 0 : month_values[g] end
result
end
如果您使用纯红宝石执行此操作,请在此上方require 'date'
。
答案 3 :(得分:0)
我建议采用以下方法:
require 'date'
def add_missing_months(dates)
# get all months
months = dates.keys.map{|m| Date.parse(m)}
# get min and max
min = months.min
max = months.max
# collect all missing months
missing_months = {}
while min < max
min = min.next_month
missing_months[min.strftime('%b %Y')] = 0 unless months.include?(min)
end
# merge hashes
dates.merge(missing_months)
end