I am trying to create the following HTML table in Rails 7:
Dec-20 | Dec-21 | |
---|---|---|
Item1 | 2 | 4 |
Item2 | 3 | 5 |
The data is stored in a normalized way:
date | item | value |
---|---|---|
Dec-20 | Item1 | 2 |
Dec-21 | Item1 | 4 |
This seems to be a fairly straightforward use case but I have not found a good example or tool explaining how to do this, either in the controller or in HTML. These articles give a partial explanation: How to group events objects under a single shared date with group_by How do I group content according to date (Rails) Any guidance would be appreciated. Particularly if there is a tool, similar to Chartkick that automates this process. Thank you.
CodePudding user response:
I find that doing the work in the controller makes the view less complicated:
assumption that your company_facts model has the following structure:
"id" integer
"cat_id" references category (id)
"date" of type date
"name" string (which is the "item")
"value" number (to be displayed in the table)
Controller:
def index
@cf = company.company_facts.where(cat_id: cat_id)
@dates = @cf.map(&:date).uniq.sort
@items = @cf.map(&:name).uniq.sort
@hash = Hash.new(0)
@cf.each_with_object(@hash) {|obj,hsh| hsh[ [obj.date, obj.name] ] = obj.value}
end
View (I prefer HAML ... hopefully you can work with this):
%table
%tr
%td
- @dates.each do |date|
%th= date
- @items.each do |item|
%tr
%th= item
- @dates.each do |date|
%td= number_with_delimiter( @hash[ [date, item] ] )
the most complicated bit in the above is creating the hash, using a key that is a combined array of the date and the item label. You could do all of the above without creating the hash, by performing lookups against your database for each entry, but if you have a large amount of data to plot, you will find creating the Hash
to be significantly quicker and lower impact.
by using Hash.new
we also specify the default return value if there is no match.
I don't know from your example where company
and cat_id
are set, so you will need to make sure that that logic also goes into your controller.