【问题标题】:Fill an array of hash with missing values用缺失值填充哈希数组
【发布时间】:2020-11-25 10:58:06
【问题描述】:

我正在尝试为ApexChart 构建一些热图细节图表。到目前为止,我得到了这个 Hash 数组,用于 3 个活动。

[{
  :name => "Activity 1",
  :data => {
    "May 2020" => 37, "June 2020" => 17, "July 2020" => 9, "August 2020" => 18
  }
}, {
  :name => "Activity 2",
  :data => {
    "May 2020" => 3
  }
}, {
  :name => "Activity 3",
  :data => {
    "July 2020" => 5, "November 2020" => 11
  }
}]

在活动 3 中,我们只有 2 个月,即JulyNovember

我的需要是填充每个哈希,所有缺失的日期,并用 0 作为值填充它们。我期待的结果是

[{
  :name => "Activity 1",
  :data => {
    "May 2020" => 37, "June 2020" => 17, "July 2020" => 9, "August 2020" => 18, "November 2020" => 0
  }
}, {
  :name => "Activity 2",
  :data => {
    "May 2020" => 3, "June 2020" => 0, "July 2020" => 0, "August 2020" => 0, "November 2020" => 0
  }
}, {
  :name => "Activity 3",
  :data => {
    "May 2020" => , "June 2020" => 0, "July 2020" => 5, "August 2020" => 0, "November 2020" => 11
  }
}]

是的,September 是故意丢失的。我想实现这一目标的最好方法是每个月一个一个地检索;然后用缺失的月份填充每个数组;但我不知道如何实现这一点。

【问题讨论】:

    标签: ruby-on-rails ruby apexcharts


    【解决方案1】:

    如果arr 是您的哈希数组,您可以分两步构造所需的数组。

    require 'date'
    
    date_fmt = "%B %Y"
    first_month, last_month = arr.flat_map do |g|
      g[:data].keys
    end.map { |s| Date.strptime(s, date_fmt) }.minmax
      #=> [#<Date: 2020-05-01 ((2458971j,0s,0n),+0s,2299161j)>,
      #    #<Date: 2020-11-01 ((2459155j,0s,0n),+0s,2299161j)>] 
    
    h = (first_month..last_month).map do |d|
      d.strftime(date_fmt)
    end.product([0]).to_h
      #=> {"May 2020"=>0, "June 2020"=>0, "July 2020"=>0, "August 2020"=>0,
      #    "September 2020"=>0, "October 2020"=>0, "November 2020"=>0} 
    
    arr.map { |g| g.merge(:data => h.merge(g[:data])) }
      #=> [
      #     {
      #       :name=>"Activity 1",
      #       :data=>{
      #         "May 2020"=>37, "June 2020"=>17, "July 2020"=>9,
      #         "August 2020"=>18, "September 2020"=>0,
      #         "October 2020"=>0, "November 2020"=>0
      #       }
      #     },
      #     {
      #       :name=>"Activity 2",
      #       :data=>{
      #         "May 2020"=>3, "June 2020"=>0, "July 2020"=>0,
      #         "August 2020"=>0, "September 2020"=>0,
      #         "October 2020"=>0, "November 2020"=>0
      #       }
      #     },
      #     {
      #       :name=>"Activity 3",
      #       :data=>{
      #         "May 2020"=>0, "June 2020"=>0, "July 2020"=>5,
      #         "August 2020"=>0, "September 2020"=>0,
      #         "October 2020"=>0, "November 2020"=>11
      #       }
      #     }
      #   ] 
    

    请参阅Enumerable#flat_mapDate::strptimeArray#minmaxDate#strftimeArray#productHash#merge。有关日期格式指令,另请参阅 DateTime#strptime

    注意在first_monthlast_month的计算中,

    [#<Date: 2020-05-01 ((2458971j,0s,0n),+0s,2299161j)>,
     #<Date: 2020-11-01 ((2459155j,0s,0n),+0s,2299161j)>].
      map { |d| d.strftime(date_fmt) }
      #=> ["May 2020", "November 2020"]
    

    【讨论】:

      【解决方案2】:

      您可以通过类似的方法来实现这一点,方法是首先获取月份,然后修改在您的情况下为哈希的数组项:

      months = data.flat_map { |d| d[:data].keys }.to_set
      data.each do |d|
        months.each do |month|
          d[:data][month] = 0 unless d[:data].key?(month)
        end
      end
      

      您还可以创建一个新的Array,其中Hash 值使用默认值进行初始化:

      data_with_default = data.map do |d|
        {
          name: d[:name],
          data: Hash.new(0).update(d[:data])
        }
      end
      

      【讨论】:

      • 接近完美,感谢您的宝贵时间。它让我更好地理解了我应该使用什么。但是,它不遵循月份的顺序。不幸的是,对于 Apex Charts,它是强制性的。这意味着用你的方法我得到了Activity 3{"July 2020" =&gt; 5, "November 2020" =&gt; 11, "May 2020" =&gt; , "June 2020" =&gt; 0, "August 2020" =&gt; 0} 但我会自己处理这个
      • Ruby Hash 实现不会保持顺序,您不应该依赖它。如果需要,您需要找到一个替代实现,例如 ActiveSupport::OrderedHash
      • 似乎 ActiveSupport::OrderedHash 已被弃用
      • 我可能误导了你:stackoverflow.com/a/31418688/221213
      • 您应该注意,您的解决方案 #1 会改变原始哈希值。代替d[:data][month] = 0 unless d[:data].key?(month),你可以写d[:data][month] ||= 0。解决方案#2 中产生的哈希值之一是h = {:name=&gt;"Activity 2", :data=&gt;{"May 2020"=&gt;3}}。虽然h[:data]["September"] #=&gt; 0 确实与拥有所有月份的哈希不同。例如,h[:data].keys #=&gt; ["May 2020"] 会产生错误的结果。
      【解决方案3】:

      尝试以下方法:

      a = [{
        :name => "Activity 1",
        :data => {
          "May 2020" => 37, "June 2020" => 17, "July 2020" => 9, "August 2020" => 18
        }
      }, {
        :name => "Activity 2",
        :data => {
          "May 2020" => 3
        }
      }, {
        :name => "Activity 3",
        :data => {
          "July 2020" => 5, "November 2020" => 11
        }
      }]
      
      months = ['May 2020', 'June 2020', 'July 2020', 'August 2020', 'November 2020']
      
      puts a.map { |h| { name: h[:name], data: Hash[months.map { |m| [m, h[:data][m] ? h[:data][m] : 0] }] } }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-25
        • 2014-01-31
        • 2014-03-02
        • 2016-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-25
        相关资源
        最近更新 更多