0

I have a helper module to generate an array hash data, which is something like:

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

So I make the method like:

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

Since the Category will always change, I try to use loop in this method like:

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

But ruby alerts me with an error:

/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...

Why does it say expecting keyword_end, and how should I fix it?


The method category_sum_by_day it calls looks like:

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

Or should I alter this method to generate a more friendly method for the helper above?

1 Answer 1

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

Unfortunately, this piece of code does not adhere to Ruby's grammar. The problem is the body of the block. x => y is not an expression and the syntax requires bodies of blocks to be expressions.

If you want to generate a hash by one key-value pair at a time try the following combination of Hash::[], Array#flatten and the splat operator (i.e. unary *):

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

As a result I'd rewrite the last expresion of records_chart_data more or less as follows

(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

If you consider it unreadable you can do it in a less sophisticated way, i.e.:

(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

Another idea is to use Array#reduce and adopt a more functional approach.

(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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.