如何将其折叠成一行

时间:2015-09-14 16:52:51

标签: ruby

我有以下ruby代码:

@averages = Array.new
weekly_averages.each do |average|

  yr = average.date.to_s[0..3].to_i
  wk = average.date.to_s[4..5].to_i
  prices = Array.new
  prices << { locale: average.locale, price: average.average }

  @averages << { :year => yr, :week => wk, :prices => prices }

end

这会导致像这样的表

Week Ending Date Product    Nairobi  Mombasa   Kisumu    Nakuru  Eldoret
38  2014-09-21  Dry Maize   3,140.00                
38  2014-09-21  Dry Maize           2,216.67            
38  2014-09-21  Dry Maize                     2,513.13      
38  2014-09-21  Dry Maize                               3,362.50    
38  2014-09-21  Dry Maize                                        2,311.43`

如何将其折叠为一行,例如:

Week Ending Date   Product  Nairobi Mombasa Kisumu  Nakuru   Eldoret
38  2014-09-21  Dry Maize 3,140   2216.67 2,513.13 3,362.50 2,311.43

2 个答案:

答案 0 :(得分:1)

您可以使用Enumerable#group_by并按已按日期分组的元素进行迭代:

@averages = weekly_averages.group_by(&:date).map do |date, averages|
  yr = date.to_s[0..3].to_i
  wk = date.to_s[4..5].to_i
  prices = averages.map { |average| { locale: average.locale, price: average.average } }
  { year: yr, week: wk, prices: prices }
end

答案 1 :(得分:0)

让我们从创建一些数据开始:

A = Struct.new :week, :ending_date, :product, :locale, :average

weekly_averages = [
  A.new(38, '2014-09-21', 'Dry Maize', 'Kakuru',  '3362.50'),
  A.new(38, '2014-09-21', 'Dry Maize', 'Mombasa', '2216.67'),
  A.new(38, '2014-09-21', 'Dry Maize', 'Kisuma',  '2513.13'),
  A.new(38, '2014-09-21', 'Dry Maize', 'Nairobi', '3140.00'),
  A.new(38, '2014-09-21', 'Dry Maize', 'Eldoret', '2311.43'),
  A.new(39, '2014-09-28', 'Dry Maize', 'Kakuru',  '3105.61'),
  A.new(39, '2014-09-28', 'Dry Maize', 'Mombasa', '2060.12'),
  A.new(38, '2014-09-21', 'Coffee',    'Eldoret', '1841.43'),
  A.new(38, '2014-09-21', 'Coffee',    'Kakuru',  '2187.66'),
]

您会看到我通过为第二个产品('Coffee')和第二周(周39)添加结果来修改您的示例。此外,并非所有语言环境都具有添加结果的值。

我假设结束日期仅取决于一周。如果是这样,我们可以使用哈希处理它(如果没有,你可以对待它,我已经处理了下面的其他属性):

week_end_date = weekly_averages.each_with_object({}) { |i,h|
  h[i.week] = i.ending_date }    
  #=> {38=>"2014-09-28", 39=>"2014-09-28"} 

你想以什么顺序排行?我假设你想要按周,按产品每周订购。 (如果您想要不同的订购,只需根据需要更改以下内容。)您想要产品的顺序是什么?按字母顺序排列?作为指定?我假设您要指定产品订单:

products = ['Dry Maize', 'Coffee']
  #=> ["Dry Maize", "Coffee"]

我们需要一个新的数据结构,其元素的排序与表的行相同,我将首先创建一个散列,其键是用于排序数据的两个属性的数组(week和{{1 }})。这使用方法Hash#update(aka product)的形式,该方法使用块(此处为merge!)来确定合并的两个哈希中存在的键的值:

{ |_,o,n| o.merge(n)}

现在使用Enumerable#sort_byaverages = weekly_averages.each_with_object({}) { |i,h| h.update([i.week, i.product]=>{ i.locale => i.average }) { |_,o,n| o.merge(n) } } #=> {[38, "Dry Maize"]=> # {"Kakuru"=>"3362.50", "Mombasa"=>"2216.67", "Kisuma"=>"2513.13", # "Nairobi"=>"3140.00", "Eldoret"=>"2311.43"}, # [39, "Dry Maize"]=>{"Kakuru"=>"3105.61", "Mombasa"=>"2060.12"}, # [38, "Coffee"]=>{"Eldoret"=>"1841.43", "Kakuru"=>"2187.66"}} 的元素进行排序,然后将结果(键值对数组)转换回哈希值;

averages

我们还有两件事需要指明。第一个是语言环境(列)的顺序:

sorted_averages = Hash[averages.sort_by { |(week, product),_|
  [week, products.index(product)] }]
  #=> {[38, "Dry Maize"]=>{"Kakuru"=>"3362.50", "Mombasa"=>"2216.67",
  #       "Kisuma"=>"2513.13", "Nairobi"=>"3140.00", "Eldoret"=>"2311.43"},
  #    [38, "Coffee"]=>{"Eldoret"=>"1841.43", "Kakuru"=>"2187.66"},
  #    [39, "Dry Maize"]=>{"Kakuru"=>"3105.61", "Mombasa"=>"2060.12"}}

最后,我们需要指定每列的宽度和对齐方式。为此,我将使用键locales = %w| Nairobi Mombasa Kisuma Kakuru Eldoret | #=> ["Nairobi", "Mombasa", "Kisuma", "Kakuru", "Eldoret"] (列标签),:label(列宽)和:width(左侧调整,居中或右侧调整)创建一个哈希数组。该数组的i th 元素对应于i th 列:

:alignment

我们现在可以打印表了。首先是列标题:

fields = [{ label: "Week", width: 4, alignment: :left},
          { label: "Ending Date", width: "Ending Date".size + 2,
             alignment: :center },
          { label: "Product",
            width: (1+(weekly_averages.map {|i|
              i.product}.uniq << "Product").map(&:size).max),
            alignment: :left }
]
  #=> [{:label=>"Week", :width=>4, :alignment=>:left},
  #    {:label=>"Ending Date", :width=>13, :alignment=>:center},
  #    {:label=>"Product", :width=>10, :alignment=>:left}] 

locales.each { |l| fields <<
  { label: l,
    width: [1+l.size, 2 + weekly_averages.map { |i| i.average.size }.max].max,
    alignment: :right
  }
}

fields
  #=> [{:label=>"Week", :width=>4, :alignment=>:left},
  #    {:label=>"Ending Date", :width=>13, :alignment=>:center},
  #    {:label=>"Product", :width=>10, :alignment=>:left},
  #    {:label=>"Nairobi", :width=>11, :alignment=>:right},
  #    {:label=>"Mombasa", :width=>11, :alignment=>:right},
  #    {:label=>"Kisumu", :width=>11, :alignment=>:right},
  #    {:label=>"Nakuru", :width=>11, :alignment=>:right},
  #    {:label=>"Eldoret", :width=>11, :alignment=>:right}] 

然后身体:

fields.each do |h|
  case h[:alignment]
  when :left           then print h[:label].ljust(h[:width])
  when :center         then print h[:label].center(h[:width])
  when :right          then print h[:label].rjust(h[:width])
  end
end
puts