在Ruby中的块中嵌套循环

时间:2012-12-17 11:04:18

标签: ruby

我有一个辅助模块来生成数组哈希数据,类似于:

[{:date => d, :total_amount => 31, :first_category => 1, :second_category => 2,...},
 {:date => d+1, :total_amount => 31, :first_category => 1, :second_category => 2,...}]

所以我制作的方法如下:

def records_chart_data(category = nil, start = 3.weeks.ago)
  total_by_day = Record.total_grouped_by_day(start)
  category_sum_by_day = Record.sum_of_category_by_day(start)

  (start.to_date..Time.zone.today).map do |date|
    {
      :date  => date,
      :total_amount => total_by_day[date].try(:first).try(:total_amount) || 0,
      Category.find(1).title => category_sum_by_day[0][date].try(:first).try(:total_amount) || 0,
      Category.find(2).title => category_sum_by_day[1][date].try(:first).try(:total_amount) || 0,
      Category.find(3).title => category_sum_by_day[2][date].try(:first).try(:total_amount) || 0,
    }
  end
end

由于类别将始终更改,我尝试在此方法中使用循环,如:

def records_chart_data(category = nil, start = 3.weeks.ago)
  total_by_day = Record.total_grouped_by_day(start)
  category_sum_by_day = Record.sum_of_category_by_day(start)

  (start.to_date..Time.zone.today).map do |date|
    {
      :date  => date,
      Category.all.each_with_index do |category, index|             
        category.title => category_sum_by_day[index][date].try(:first).try(:total_amount) || 0,
      end
      :total_amount => total_by_day[date].try(:first).try(:total_amount) || 0
    }
  end
end

但是红宝石警告我一个错误:

/Users/tsu/Code/CashNotes/app/helpers/records_helper.rb:10: syntax error, unexpected tASSOC, expecting keyword_end
      category.title => category_sum_by_day[index][d...

为什么会说expecting keyword_end,我应该如何解决?


它调用的方法category_sum_by_day如下:

def self.sum_of_category_by_day(start)
  records = where(date: start.beginning_of_day..Time.zone.today)
  records = records.group('category_id, date(date)')
  records = records.select('category_id, date, sum(amount) as total_amount')
  records = records.group_by{ |r| r.category_id }
  records.map do |category_id, value|
    value.group_by {|r| r.date.to_date} 
  end
end

或者我应该改变这个方法来为上面的助手生成一个更友好的方法吗?

1 个答案:

答案 0 :(得分:2)

Category.all.each_with_index do |category, index|             
  category.title => category_sum_by_day # ...snip!
end

不幸的是,这段代码并不符合Ruby的语法。问题是块体。 x => y 不是表达式,语法要求块体是表达式。

如果您想一次按一个键值对生成哈希,请尝试以下Hash::[]Array#flatten和splat运算符(即一元*)的组合:< / p>

Hash[*5.times.map { |i| [i * 3, - i * i] }.flatten]

因此,我会更多或更少地重写records_chart_data的最后一次表达,如下所示

(start.to_date..Time.zone.today).map do |date|
  categories = Hash[*Category.all.each_with_index do |category, index|
    [ category.title, category_sum_by_day[...] ]
  end .flatten]

  { :date  => date,
    :total_amount => total_by_day[date].try(:first).try(:total_amount) || 0
  }.merge categories
end

如果你认为它不可读,你可以用一种不太复杂的方式来做,即:

(start.to_date..Time.zone.today).map do |date|
  hash = {
    :date  => date,
    :total_amount => total_by_day[date].try(:first).try(:total_amount) || 0
  }
  Category.all.each_with_index do |category, index|
    hash[category.title] = category_sum_by_day[...]
  end
  hash
end

另一个想法是使用Array#reduce并采用更具功能性的方法。

(start.to_date..Time.zone.today).map do |date|
  Category.all.each_with_index.reduce({
    :date  => date,
    :total_amount => total_by_day[date].try(:first).try(:total_amount) || 0
  }) do |hash, (category, index)|
    hash.merge category.title => category_sum_by_day[...]
  end
  hash
end