【问题标题】:Listing missing months in an array of dates列出日期数组中缺失的月份
【发布时间】:2015-06-29 11:14:51
【问题描述】:

我有一个数组中的交易列表。

=> [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]

提前致谢!

【问题讨论】:

    标签: ruby arrays date methods


    【解决方案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"]
    

    【讨论】:

      【解决方案2】:

      编辑:对于已经由日期对象组成的数组,您可以这样做:

      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"]
      

      【讨论】:

      • 很好,希德。巧妙使用parse。例如,有一个Date#next_month 方法!。我浪费了很多时间自己动手。我看你也可以用Date@&gt;&gt;写,看起来很酷。
      • 谢谢 Cary,当我第一次发现 Date#next_month 方法时,我也有同样的感觉!
      • 问题 8..-1 是否代表开始月份?因为一年中的开始月份将是不同的时间。
      • @SupremeA [8..-1] 获取初始数组中每个日期的倒数第八个字符,因此它只返回每个日期的月份和年份。例如,对于数组中的第一个日期,它将返回“2013 年 10 月”,第二个:“2013 年 11 月”,第三个:“2013 年 11 月”等等。
      • @Sid 我收到错误 NoMethodError: undefined method '[ ]' for Tue, 01 Oct 2013:Date 当我到达 d= 行时,[8..-1] 部分似乎有问题
      【解决方案3】:

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

      代码

      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
      

      【讨论】:

        猜你喜欢
        • 2020-02-08
        • 2021-06-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多