【问题标题】:Populate missing entries when comparing arrays in Ruby在 Ruby 中比较数组时填充缺失的条目
【发布时间】:2018-01-07 10:16:23
【问题描述】:

我有三个带有计数的日期数组:

first = [["July 01", "2"]["July 03", "2"]]
second = [["June 30", "2"]["July 01", "2"]["July 02", "2"]]
third = [["July 01", "2"]["July 02", "2"]]

我正在尝试(没有成功)比较三个数组,获取完整的日期范围,并将缺失的 0 结果注入到其他数组中......这样最后每个数组都会有开始日期的条目6 月 30 日到 7 月 3 日,像这样:

first = [["June 30", "0"]["July 01", "2"]["July 02", "0"]["July 03", "2"]]
second = [["June 30", "2"]["July 01", "2"]["July 02", "2"]["July 03", "0"]]
third = [["June 30", "0"]["July 01", "2"]["July 02", "2"]["July 03", "0"]]

我尝试了一堆非常复杂的比较(例如进行推导,存储为新数组,然后使用该数组添加到缺少的数组,但是当要比较的数组超过两个时,它变得非常复杂)和注入要做到这一点,但我认为必须有一个相对简单的方法来使用 Ruby 或 Rails 做到这一点。有什么想法吗?

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    这是另一种方式,使用Date

    require 'date'
    
    def compare_dates(*items)
      all_dates = items.flatten(1).map { |d| Date.parse(d.first) }
      str_dates = (all_dates.min..all_dates.max).map { |d| d.strftime("%B %d") }
    
      items.map do |arr|
        str_dates.map do |date|
          current = arr.select { |e| e[0] == date }.flatten
          current.empty? ? [date, "0"] : current
        end
      end
    end
    
    compare_dates(first, second, third)
    #=> [[["June 30", "0"], ["July 01", "2"], ["July 02", "0"], ["July 03", "2"]],
    #    [["June 30", "2"], ["July 01", "2"], ["July 02", "2"], ["July 03", "0"]],
    #    [["June 30", "0"], ["July 01", "2"], ["July 02", "2"], ["July 03", "0"]]]
    

    如果你想覆盖每个数组的值,你可以这样做:

    first, second, third = compare_dates(first, second, third)
    
    first
    #=> [["June 30", "0"], ["July 01", "2"], ["July 02", "0"], ["July 03", "2"]]
    
    second
    #=> [["June 30", "2"], ["July 01", "2"], ["July 02", "2"], ["July 03", "0"]]
    
    third
    #=> [["June 30", "0"], ["July 01", "2"], ["July 02", "2"], ["July 03", "0"]]
    

    【讨论】:

    • 干净、简洁、美观。谢谢你,格里。
    【解决方案2】:

    我有一个问题,为什么这是一组数组?如果你能把它变成一个哈希,这个问题就很容易处理了。它可能看起来像这样:

    first = {"july 01" => 2, "july 02" => 1}
    second = {"june 31" => 1, "july 01" => 1}
    
    
    keys = first.keys
    keys << second.keys
    
    keys.each do |key|
      first[key] = first[key] || 0
    end
    

    我没有对此进行测试,这可能不是最有效的方法,但您可以在此基础上进行优化。我希望这会有所帮助。

    您也可以使用类似Convert array of 2-element arrays into a hash, where duplicate keys append additional values 的方式将其转换为哈希。

    【讨论】:

    • 谢谢,但这并没有真正的帮助。我已经写了一些可以处理两个数组(或哈希,没关系)的东西。我需要一种方法来比较三个或更多数组并返回所需的结果,而不会太复杂。我想我可能一开始就缺少核心 ruby​​ 方法。
    【解决方案3】:

    您可以取所有数组的并集,然后从中计算。

    all = first | second | third
     #=> [["July 01", "2"], ["July 03", "2"], ["June 30", "2"], ["July 02", "2"]]
    
    (first | all).map { |k, v| first.include?([k, v]) ? [k, v] : [k, "0"] }
                 .sort_by { |i| [ Time.new(0, i[0][0..2]).month, i[0][-2..-1] ] }
    
     #=> [["June 30", "0"], ["July 01", "2"], ["July 02", "0"], ["July 03", "2"]]
    

    【讨论】:

      【解决方案4】:
      arr = [[["July 01", "2"], ["July 03", "2"]],
             [["June 30", "2"], ["July 01", "2"], ["July 02", "2"]],
             [["July 01", "2"], ["July 02", "2"]]]
      
      require 'date'
      
      default = arr.flatten(1).
                    map(&:first).
                    uniq.
                    sort_by { |s| Date.strptime(s, '%B %d') }.
                    product(['0']).
                    to_h
        #=> {"June 30"=>"0", "July 01"=>"0", "July 02"=>"0", "July 03"=>"0"}
      
      arr.map { |a| default.merge(a.to_h).to_a }
        #=> [[["June 30", "0"], ["July 01", "2"], ["July 02", "0"], ["July 03", "2"]],
        #    [["June 30", "2"], ["July 01", "2"], ["July 02", "2"], ["July 03", "0"]],
        #    [["June 30", "0"], ["July 01", "2"], ["July 02", "2"], ["July 03", "0"]]]
      

      步骤如下。

      b = arr.flatten(1)
        #=> [["July 01", "2"], ["July 03", "2"], ["June 30", "2"], ["July 01", "2"],
        #    ["July 02", "2"], ["July 01", "2"], ["July 02", "2"]]
      c = b.map(&:first)
        #=> ["July 01", "July 03", "June 30", "July 01", "July 02", "July 01", "July 02"]
      d = c.uniq
        #=> ["July 01", "July 03", "June 30", "July 02"]
      e = d.sort_by { |s| Date.strptime(s, '%B %d') }
        #=> ["June 30", "July 01", "July 02", "July 03"] 
      f = e.product(['0'])
        #=> [["June 30", "0"], ["July 01", "0"], ["July 02", "0"], ["July 03", "0"]]
      default = f.to_h
        #=> {"June 30"=>"0", "July 01"=>"0", "July 02"=>"0", "July 03"=>"0"}
      

      计算

      arr.map { |a| default.merge(a.to_h).to_a }
      

      arr 的第一个值被传递给块,块变量a 设置为等于该值并执行块计算。

        a = arr.first
          #=> [["July 01", "2"], ["July 03", "2"]]
        g = a.to_h
          #=> {"July 01"=>"2", "July 03"=>"2"}
        h = default.merge(g)
          #=> {"June 30"=>"0", "July 01"=>"2", "July 02"=>"0", "July 03"=>"2"}
        h.to_a
          #=> [["June 30", "0"], ["July 01", "2"], ["July 02", "0"], ["July 03", "2"]]
      

      arr 的其他值的计算类似。

      d 的计算参见Date::strptime

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-03-02
        • 2016-08-26
        • 2019-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-22
        相关资源
        最近更新 更多