在日期数组中列出缺少的月份

时间:2015-04-22 02:29:32

标签: ruby arrays date methods

我有一个数组中的事务列表。

=> [Wed, 23 Oct 2013, Mon, 18 Nov 2013, Fri, 22 Nov 2013, Mon, 13 Jan 2014, Tue, 28 Jan 2014, Mon, 03 Feb 2014, Mon, 10 Feb 2014, Tue, 18 Feb 2014, Fri, 07 Mar 2014, Mon, 31 Mar 2014, Mon, 07 Apr 2014, Tue, 10 Jun 2014, Mon, 30 Jun 2014, Mon, 22 Sep 2014, Mon, 06 Oct 2014, Fri, 14 Nov 2014, Tue, 18 Nov 2014, Fri, 26 Dec 2014, Thu, 15 Jan 2015, Mon, 23 Mar 2015, Mon, 20 Apr 2015]

我需要比较每笔交易的日期,并列出月份和年份列表中缺少的任何月份。这就是我现在拥有的......

@find_transactions = (@user.transactions.find_all { |t| (t.name 'name' })
@trans_dates = @find_transactions.map(&:date).sort!.map { |s| Date.strptime(s, '%Y-%m') }.each_cons(2).map{ |d1,d2| d1.next_month == d2 }

如果每个月都有,这个方法目前给我一个真或假,但我需要让方法打印一个缺少的月份列表。我想让它一起打印月份和年份。

以下是此方法给我的回复......

=> [true, false, true, false, false, true, false, true, false, false, false, true, true]

我想要这样的回复...

=> [March 2015, December 2014, September 2014]

提前致谢!

3 个答案:

答案 0 :(得分:3)

编辑:对于已经由日期对象组成的数组,您可以执行以下操作:

require 'date'

dates = [Wed, 23 Oct 2013, Mon, 18 Nov 2013, Fri, 22 Nov 2013, Mon, 13 Jan 2014, Tue, 28 Jan 2014, Mon, 03 Feb 2014, Mon, 10 Feb 2014, Tue, 18 Feb 2014, Fri, 07 Mar 2014, Mon, 31 Mar 2014, Mon, 07 Apr 2014, Tue, 10 Jun 2014, Mon, 30 Jun 2014, Mon, 22 Sep 2014, Mon, 06 Oct 2014, Fri, 14 Nov 2014, Tue, 18 Nov 2014, Fri, 26 Dec 2014, Thu, 15 Jan 2015, Mon, 23 Mar 2015, Mon, 20 Apr 2015]

all_dates = []
dates.first.upto(dates.last) {|x| all_dates << x.strftime('%b %Y') if x.day == 1 || x == dates.first}

d = dates.map {|x| x.strftime('%b %Y')}.uniq

p (all_dates - d)
  #=> ["Dec 2013", "May 2014", "Jul 2014", "Aug 2014", "Feb 2015"]

编辑:以下方法适用于日期字符串数组

你可以试试这个:

require 'date'

dates = ["Wed, 23 Oct 2013", "Mon, 18 Nov 2013", "Fri, 22 Nov 2013", "Mon, 13 Jan 2014", "Tue, 28 Jan 2014", "Mon, 03 Feb 2014", "Mon, 10 Feb 2014", "Tue, 18 Feb 2014", "Fri, 07 Mar 2014", "Mon, 31 Mar 2014", "Mon, 07 Apr 2014", "Tue, 10 Jun 2014", "Mon, 30 Jun 2014", "Mon, 22 Sep 2014", "Mon, 06 Oct 2014", "Fri, 14 Nov 2014", "Tue, 18 Nov 2014", "Fri, 26 Dec 2014", "Thu, 15 Jan 2015", "Mon, 23 Mar 2015", "Mon, 20 Apr 2015"]

all_dates = []
d = dates.map {|x| Date.parse(x[8..-1])}.uniq
counter = d.first

until counter == d.last
  all_dates << counter
  counter = counter.next_month
end


p (all_dates - d).map {|x| x.strftime('%b %Y')}
  #=> ["Dec 2013", "May 2014", "Jul 2014", "Aug 2014", "Feb 2015"]

另一种(更简洁)的方式是:

require 'date'

dates = ["Wed, 23 Oct 2013", "Mon, 18 Nov 2013", "Fri, 22 Nov 2013", "Mon, 13 Jan 2014", "Tue, 28 Jan 2014", "Mon, 03 Feb 2014", "Mon, 10 Feb 2014", "Tue, 18 Feb 2014", "Fri, 07 Mar 2014", "Mon, 31 Mar 2014", "Mon, 07 Apr 2014", "Tue, 10 Jun 2014", "Mon, 30 Jun 2014", "Mon, 22 Sep 2014", "Mon, 06 Oct 2014", "Fri, 14 Nov 2014", "Tue, 18 Nov 2014", "Fri, 26 Dec 2014", "Thu, 15 Jan 2015", "Mon, 23 Mar 2015", "Mon, 20 Apr 2015"]

all_dates = []
d = dates.map {|x| Date.parse(x[8..-1])}.uniq

d.first.upto(d.last) {|x| all_dates << x if x.day == 1}

p (all_dates - d).map {|x| x.strftime('%b %Y')}
  #=> ["Dec 2013", "May 2014", "Jul 2014", "Aug 2014", "Feb 2015"]

答案 1 :(得分:2)

这是你可以做到的一种方式。

<强>代码

require 'date'

def missing_months(dates)    
  a = dates.map { |s| d = Date.strptime(s, '%a, %d %b %Y'); d - d.day + 1 }
  (all_months_in_range(*a.minmax) -a).map { |d| d.strftime('%b %Y') } 
end

def all_months_in_range(f,l)
  (12*(l.year-f.year)+l.month-f.month+1).times.map do |i|
    y,m = (f.month+i).divmod(12)
    y += f.year
    (m=12; y-=1) if m ==0
    Date.new(y,m)
  end
end

示例

dates = ['Wed, 23 Oct 2013', 'Mon, 18 Nov 2013', 'Fri, 22 Nov 2013',
         'Fri, 14 Nov 2014', 'Tue, 18 Nov 2014', 'Fri, 26 Dec 2014',
         'Mon, 13 Jan 2014', 'Tue, 28 Jan 2014', 'Mon, 03 Feb 2014',
         'Mon, 31 Mar 2014', 'Mon, 07 Apr 2014', 'Tue, 10 Jun 2014',
         'Mon, 30 Jun 2014', 'Mon, 22 Sep 2014', 'Mon, 06 Oct 2014',
         'Mon, 10 Feb 2014', 'Tue, 18 Feb 2014', 'Fri, 07 Mar 2014',
         'Thu, 15 Jan 2015', 'Mon, 23 Mar 2015', 'Mon, 20 Apr 2015']

missing_months(dates)     
  #=> ["Dec 2013", "May 2014", "Jul 2014", "Aug 2014", "Feb 2015"] 

请注意,dates无需排序。

<强>解释

对于上面的示例:

  a = dates.map { |s| d = Date.strptime(s, '%a, %d %b %Y'); d - d.day + 1 }
    #=> [#<Date: 2013-10-01 ((2456567j,0s,0n),+0s,2299161j)>,
    #    #<Date: 2013-11-01 ((2456598j,0s,0n),+0s,2299161j)>,
    #   ...
    #    #<Date: 2015-04-01 ((2457114j,0s,0n),+0s,2299161j)>] 

请注意,每个日期都在该月的第一天。接下来,获取这些日期的第一个和最后一个:

  f,l = a.minmax
  f #=> [#<Date: 2013-10-01 ((2456567j,0s,0n),+0s,2299161j)>,
  l #=>  #<Date: 2015-04-01 ((2457114j,0s,0n),+0s,2299161j)>]

现在将fl传递给all_months_in_range,以创建一个数组,其中包含fl之间每个月第一天的日期对象

  b = all_months_in_range(f,l) 
    #=> [#<Date: 2013-10-01 ((2456567j,0s,0n),+0s,2299161j)>,
    #    #<Date: 2013-11-01 ((2456598j,0s,0n),+0s,2299161j)>,
    #    ...
    #    #<Date: 2015-04-01 ((2457114j,0s,0n),+0s,2299161j)>] 

  b.size #=> 19 

我将跳过对这个辅助方法的解释,因为它非常简单。

计算数组ba之间的差异,以获取缺少的月初日期:

  c = b-a
    #=> [#<Date: 2013-12-01 ((2456628j,0s,0n),+0s,2299161j)>,
    #    #<Date: 2014-05-01 ((2456779j,0s,0n),+0s,2299161j)>,
    #    #<Date: 2014-07-01 ((2456840j,0s,0n),+0s,2299161j)>,
    #    #<Date: 2014-08-01 ((2456871j,0s,0n),+0s,2299161j)>,
    #    #<Date: 2015-02-01 ((2457055j,0s,0n),+0s,2299161j)>] 

最后,将这些日期转换为所需的格式:

 c.map { |d| d.strftime('%b %Y') } 
   #=> ["Dec 2013", "May 2014", "Jul 2014", "Aug 2014", "Feb 2015"]

附录:在阅读@Sid的答案之后,我发现使用Date#next_month我可以帮助自己在助手方法中省去一些麻烦:

def all_months_in_range(f,l)
  (12*(l.year-f.year)+l.month-f.month+1).times.map { |i| f.next_month(i) }
end

答案 2 :(得分:1)

这不是很优雅,但它有效。我从原始代码@SupremeA开始,并以此为基础构建。

require 'date'

dates = ['Wed, 23 Oct 2013', 'Mon, 18 Nov 2013', 'Fri, 22 Nov 2013', 'Mon, 13 Jan 2014', 'Tue, 28 Jan 2014', 'Mon, 03 Feb 2014', 'Mon, 10 Feb 2014', 'Tue, 18 Feb 2014', 'Fri, 07 Mar 2014', 'Mon, 31 Mar 2014', 'Mon, 07 Apr 2014', 'Tue, 10 Jun 2014', 'Mon, 30 Jun 2014', 'Mon, 22 Sep 2014', 'Mon, 06 Oct 2014', 'Fri, 14 Nov 2014', 'Tue, 18 Nov 2014', 'Fri, 26 Dec 2014', 'Thu, 15 Jan 2015', 'Mon, 23 Mar 2015', 'Mon, 20 Apr 2015']

new_dates = []
dates.each { |d| new_dates.push(Date.parse(d).strftime('%B %Y')) }
sorted_dates = new_dates.map { |s| Date.strptime(s, '%B %Y') }.sort.uniq
missing_months = []
sorted_dates.each_cons(2) do |d1,d2| 
  d = d1
  while d.next_month != d2 
    missing_months.push(d.next_month.strftime('%B %Y')) 
    d = d >> 1
  end 
end
p missing_months

=> ["December 2013", "May 2014", "July 2014", "August 2014", "February 2015"]