哈希获得空月

时间:2017-08-19 12:54:05

标签: ruby-on-rails ruby hash

我有一个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}

4 个答案:

答案 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 >> 1Date#>>将日期推进其参数给出的月数,此处为1

除了:>>之外,请参阅方法的文档Integer#timesHash#fetchDate::strptimeDate#strftimeDate#year,{{ 3}},Date#monthEnumerator#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