Rails - Group using 2 tables and count - ruby-on-rails

I have the following models: students , groups_by_student and groups.
A row of students table is city_id, so I have to show an html table
Total group1 group2 group3
city1 30 10 5 15
city2 2 0 0 2
city3 20 10 10 0
city4 5 0 5 0
city5 10 0 2 8
This is what I did:
groups = Group.find([1,4,6]) #Array of groups id's
total = []
groups.each do |g|
total << Student.joins(:groups_by_students).where(:groups_by_students => {:group_id => g.descendants}).count(:group => :city_id)
end
#I'm using AwesomeNestedSet gem, so g.descendants gives group children.
So now I have an array of 3 hashes that contain the city id as key and the total of students as the value, but now I'm not sure how to present this data in a html table.
How can I iterate per each "total" element? or is there another way of getting this information?
Thanks in advance
Javier
EDIT:
This is the total array
total = [
{city1 =>10, city3 => 10},
{city1 => 5, city3=>10, city4=>5, city5 => 2},
{city1 => 15, city2 => 2}
]
and now I have to place each in a td label inside a html table with the 0 if theres no value for that group.

I've traversed an array of hashes like;
ary.each do |hash| puts "<tr>#{hash.keys} : #{hash.values}</tr>" end
Can you hack that to suit your needs? Am afraid your question doesn't provide a lot to work with.

This is what i did, may be it might help you a little bit: (here the total value is the last column though)
<table>
<% i = 1%>
<% total = 0%>
<% city=""%>
<% 5.times do %>
<tr>
<% city = 'city'+ "#{i}" %>
<% #total.each do |hash| %>
<% if(hash[city].nil?)%>
<% hash[city] = 0 %>
<%end%>
<% total += hash[city].to_i %>
<td><%= hash[city] %></td>
<%end %>
<td> <%= total %></td>
<% total = 0 %>
<% i += 1 %>
</tr>
<%end%>
</table>
Here the row is controlled by city and not the group. Hence i could not find any other way other than a double loop. If you need that total to be printed in the first column and then rest of the information next, then i think you need to display the total first and then loop again and display city values of each group
Also, for this you need to know the number of cities before hand or else we will not know to print '0' for a particular city in a particular group

Related

Ruby on Rails undefined method `delete_at' for "John":String

I am trying to replace an array of names for a column of names out of a database.
I am new to Ruby on rails so it could be something simple.
This works fine:
<% students = %w(John Paul Ringo George) %>
<% teams = RoundRobinTournament.schedule(students) %>
<td><%= teams %></td>
This i get an error:
<% #players.each do |player| %>
<% students = player.first_name %>
<% teams = RoundRobinTournament.schedule(students) %>
<td><%= teams %></td>
Error:
undefined method `delete_at' for "John":String
Did you mean? delete
delete!
In the RoundRobinModule the "delete_at" what causes the error:
require 'round_robin_tournament/version'
module RoundRobinTournament
def self.schedule(array)
array.push(nil) if array.size.odd?
n = array.size
1.step(n / 2, 1).each do |i|
array.insert(n - i, array.delete_at(i))
end
pivot = array.pop
games = (n - 1).times.map do
day = [[array.first, pivot]] + (1...(n / 2)).map { |j| [array[j], array[n - 1 - j]] }
array.rotate!
day
end
array.push pivot unless pivot.nil?
games
end
end
students is supposed to be an array, yet students = player.first_name makes it a string.
I don't know if this is want you want, but the following should work:
<% students = #players.pluck(:first_name) %> # or #players.map(&:first_name)
<% teams = RoundRobinTournament.schedule(students) %>
<td><%= teams %></td>
Use .pluck if #players holds an ActiveRecord::Relation or .map if it's an array of Player objects.
But, the error tells you exactly what & where the problem is. You just need to pay attention. The code would have crashed earlier but both String and Array respond to .size.

ruby - Order a group_by collection desc?

I have a collection of products users have purchased, grouped by their name, so I can count the number of unique products and how many of each has been purchased:
Controller:
#line_items = Spree::LineItem.joins(:order).where(spree_orders: {state: "complete"})
#products = #line_items.group_by(&:name)
View:
<% #products.each do |name, line_items| %>
<%= name %> - <%= line_items.count %><br>
<% end %>
Is there a way to order the .each loop so that it descends by line_items.count?
Thanks
It will perform better getting the correct data directly from the db:
#products = #line_items.group(:name).order("count_all DESC").count
That will give you the names and counts directly, e.g.
# => { "line_1" => 3, "line_2" => 2, "line_3" => 8 }
There's a bit of Rails magic at work here: the SQL generated using group, order and count will look like:
SELECT COUNT(*) AS count_all, name AS name FROM spree_line_items GROUP BY name ORDER BY count_all DESC
That's where count_all comes from: Rails attaches it to the count column automatically.
Then you can plug this directly into your view:
<% #products.each do |name, line_item_count| %>
<%= name %> - <%= line_item_count %><br>
<% end %>
Alternatively, if you're using the instance variable elsewhere, here's a simple Ruby solution:
#products = #line_items.group_by(&:name).sort_by { |_k, line_items| line_items.count }.reverse
This simply uses sort_by to get records ordered by the relevant count, then reverses to get decending order. There's a good benchmark on doing this here.
Hope that helps - let me know how you get on / if you've any questions.

Find matching values of 2 columns in Ruby on Rails

I am reading a CSV file with RoR and printing out an HTML table but need to mark when the values of 2 separate columns match with another row.
The CSV looks like
name | value1 | value2| value3
bob | 2 | 3 | foo
jim | 4 | 5 | bar
tim | 2 | 7 | foo
I want to find when VALUE1 and VALUE3 match the values of VALUE1 and VALUE3 of another row in this CSV file (in this case: "2" & "foo" match on bob and tim)
The result would be something like:
name | value1 | value2| value3 | duplicate
bob | 2 | 3 | foo | Y
jim | 4 | 5 | bar | N
tim | 2 | 7 | foo | Y
I'm printing the table out
<% file.each do |row| %>
<tr>
<% row.each do |k, v| %>
<td><% v %></td>
<% end %>
</tr>
<% end %>
What I'd like to do is flag the table row when I find the matching columns.
Another version of the answer above, in plain ruby rather than ERB, so it's easier to test
I wanted to show my small ruby program to show how I did this. It's not as easy to read in the ERB version and this can be copied and pasted into IRB or piped into a file.
puts '<head></head>'
puts '<body>'
puts '<table>'
puts '<tr><th>#</th><th>col1</th><th>col2</th><th>col3</th><th>col4</th><th>Matching Previous Rows:</th></tr>'
file = [%w(Bob foo 32 3), %w(Joe zip 4 foo), %w(Joe baz 4 foo), %w(Steve foo 44 3), %w(Bob baz 32 foo), %w(Mary baz 4 wow), %w(Lisa 34 wow 2), %w(Art 45 foo E),%w(Bob foo 32 3)]
output_file = []
file.each do |row|
row.unshift 0x00FFFF #make first value the background color of 0xFFFFFF
row.push '' #add a column for matches
output_file.each_with_index do |prev_row,i| # look for current row values in previous entries
if prev_row[2] == row[2] and prev_row[4] == row[4]
if prev_row[0] == 0x00FFFF
prev_row[0] = rand(0xFFFF0)
row[0] = prev_row[0]
else
row[0] = prev_row[0]
end
row[-1] += "#{(i + 1)}, "
end
end
output_file << row #add the new row to the look-back array
end
output_file.each_with_index do |output_row,i|
print "<tr bgcolor=##{output_row[0].to_s(16).upcase}F >"
output_row.shift
print "<td>#{i + 1}</td>"
output_row.each do |v| #why the k? you don't use it here.
print '<td>' + "#{v}" + ' </td>'
end
puts '</tr>'
end
puts '</table>'
puts '</body>'
I am reminded of a certain XKCD comic about determining if a photo was taken in a National Park,
And if the photo is of a bird... LOL
So you need a bit more complex data structure to do comparison of each row with all previous rows.
OK, final edit: I added code to only set a new color if this is the first time a set of values repeats, that way if it repeats again it will get the same color. Also added code that appends columns showing what rows also match.
<% output_file = [] %>
<% color_offset = 26214 %>
<% file.each do |row| %>
<% row.unshift 0x00FFFF # add last 4 digits of bgcolor as first item in each row %>
<% row.push '' #add a column at the end for row match numbers
<% output_file.each_with_index do |prev_row,i| # look for current row values in previous entries %>
<% if prev_row[2] == row[2] and prev_row[4] == row[4] %>
<% if prev_row[0] == 0x00FFFF %>
<% prev_row[0] -= color_offset %>
<% row[0] = prev_row[0] %>
<% else %>
<% row[0] = prev_row[0] %>
<% end %>
<% color_offset -= 52 %>
<% row[-1] += "#{(i + 1)}, " %>
<% end %>
<% end %>
<% output_file << row # add the new row to the look-back array %>
<% end %>
<% output_file.each do |output_row| %>
<tr bgcolor=#FF<% output_row[0].to_s(16).upcase %> >
<% output_row.shift %>
<% output_row.each do |v| # why the k? you don't use it here. %>
<td> <% v %> </td>
<% end %>
</tr>
<% end %>
So you take each row, loop through the array of rows previously checked, if it matches change both their first items to a matching 4 digit color in hex (G and B, R is added at the end), then copy the new row into the array of rows previously checked.
This breaks if a row matches more than once, as it will only give the same color to the new match and the one previous. BUT I added a final column that tells you which row each one matched previously. So if it is highlighted you can jump back to that row.
There are other ways to do the highlighting, maybe copy the new color forward, that way more than two of the exact same row would be the same color.

Printing Specific Data From Table Using Ruby

This is my first time posting a question on this site; be gentle, please. This isn't homework. I'll try to be as concise as possible.
I have a table with 5 keyword columns, a date column, and a user ID column for identifying what user added that specific row of data. My goal is to print and count the number of matched keywords from a row iff:
1) Another row(s) contains those keywords AND has the same date.
2) The user ID for each row is unique.
For example:
Row1=> keyword1:(K1) keyword2:(K2) keyword3:(K3) keyword4:(K4) keyword5:(K5) date:(D1) user_id:(U1)
Row2=> keyword1:(K6) keyword2:(K7) keyword3:(K1) keyword4:(K2) keyword5:(K8) date:(D1) user_id:(U2)
Row3=> keyword1:(K6) keyword2:(K7) keyword3:(K1) keyword4:(K2) keyword5:(K8) date:(D2) user_id:(U2)
Row4=> keyword1:(K1) keyword2:(K2) keyword3:(K3) keyword4:(K4) keyword5:(K5) date:(D2) user_id:(U3)
Output:
K1 (2 times), K2 (2 times), on D1
K1 (2 times), K2 (2 times), on D2
Row3 should be excluded from the first count because even though the words matched, the user was a duplicate.
Here's how I've started:
<% #prophecies.each do |prophecy| %>
<% date1 = prophecy.datetwo %>
<% #prophecies.each do |prophecy| %>
<% if date1.eql?(prophecy.datetwo) == true %>
<tr>
<td><%= prophecy.keyone %></td>
<td><%= prophecy.keytwo %></td>
<td><%= prophecy.keythree %></td>
<td><%= prophecy.keyfour %></td>
<td><%= prophecy.keyfive %></td>
</tr>
<% end %>
<% end %>
<% end %>
But it's nowhere near what I'd like to accomplish. If anyone could even help me sort out the pseudocode for this I'd be happy.
First of all, it's better to do it in your model, not in your view.
You can define methods in your prophecy class something like that
def self.keywords_at_day(date)
#find prophecies for particular dae
prophecies = self.find_by(date:date)
# create hash with keywords for user
user_keywords = {}
prophecies.each do |prophecy|
user_keywords[prophecy.user] ||= []
user_keywords[prophecy.user] << prophecy.keyword
end
# create general array with all keywords
keywords = []
user_keywords.each do |user, kw|
keywords << kw.uniq
end
# count keywords
keywords_count = {}
keywords.each do |keyword|
keywords_count[keyword] ||= 0
keywords_count[keyword] += 1
end
keywords_count
end
def self.keywoards_lists
dates = Prophecy.all.map{|p| p.date}.uniq
keywords_lists = {}
dates.each do |date|
keywords_lists[date] = keywords_at_day(date)
end
keywords_lists
end
You controller
def index
#keywords_lists = Prophecy.keywords_lists
end
View
<%= #keywords_lists.each do |date, keywords_list| %>
<tr>
<td> <%= date %> </td>
<td>
<%= keywords_list.each do |keyword, count| %>
<%= "#{keyword} encountered #{count} times" %>
<% end %>
</td>
</tr>
<% end %>
If it's really not homework, I suggest you to read about MVC, ruby name convention(date_two instead of datetwo), booleans (you don't need "==true" for eql?).
And besides all it's not good type of question for stackoverflow
Cheers
My suggestion is to create a data structure that match your need. BAsically a hash where key is keyword, and content the list of uid that uniquely use your keyword:
r=[]<<{:keyword1=>"K1",keyword2:"K2",keyword3:"K3",keyword4:"k4",:date=>d1,uid:1}
r<<{:keyword1=>"K6",keyword2:"K7",keyword3:"K1",keyword4:"k2",:date=>d1,uid:2}
r<<{:keyword1=>"K6",keyword2:"K7",keyword3:"K1",keyword4:"k2",:date=>d2,uid:2}
(just for test purposes)
then:
r.inject({}) do |sum,aRow|
# loop for all keywords in the row.
[:keyword1,:keyword2,:keyword3,:keyword4,:keyword4].each do |keyword|
# get each entry or create it
sum[aRow[keyword]]=elem=sum[aRow[keyword]]||{}
count=elem[aRow[:date]]||{:users=>Set.new}
count[:users]<<aRow[:uid]
elem[aRow[:date]]=count
end
sum
end
The result if the following:
=> {"K1"=>
{2014-09-04 15:05:21 +0200=>{:users=>#<Set: {1, 2}>},
2014-01-01 00:00:00 +0100=>{:users=>#<Set: {2}>}},
"K2"=>{2014-09-04 15:05:21 +0200=>{:users=>#<Set: {1}>}},
"K3"=>{2014-09-04 15:05:21 +0200=>{:users=>#<Set: {1}>}},
"k4"=>{2014-09-04 15:05:21 +0200=>{:users=>#<Set: {1}>}},
"K6"=>
{2014-01-01 00:00:00 +0100=>{:users=>#<Set: {2}>},
2014-09-04 15:05:21 +0200=>{:users=>#<Set: {2}>}},
"K7"=>
{2014-01-01 00:00:00 +0100=>{:users=>#<Set: {2}>},
2014-09-04 15:05:21 +0200=>{:users=>#<Set: {2}>}},
"k2"=>
{2014-01-01 00:00:00 +0100=>{:users=>#<Set: {2}>},
2014-09-04 15:05:21 +0200=>{:users=>#<Set: {2}>}}
So you know that keyword "K1" has been used with two different date, and by two users (1,2) for first one, and 2 for second date.
Than, it will be easy to display this array.

How do I do a grouping by year?

I have a books model with a date type column named publish_date. On my views I'm iterating through the books and I want to group the books by year such that I have a heading for every year and books that were published on that year to be listed below the year heading.
So by starting with "2010" all books published on 2010 would be listed, then another heading "2009" with all books published in 2009 listed below it and so forth.
<% #all_books.each do |book| %>
<%=link_to book.title + ", (PDF, " + get_file_size(book.size) + ")" %>
<% end %>
By doing a book.publish_date.strftime("%Y") I am able to get the year but I do not know how to group the entries by year. Any help on this would be appreciated.
You can use group_by (see API) like (of the top of my head
<% #all_books.group_by(&:year).each do |year, book| %>
...
<% end %>
def year
self.created_at.strftime('%Y')
end
< % #all_books.group_by(&:year).each do |year, book| %>
Year < %= year %>
# render books here
< % end %>
What say?
You can use group_by for convenience, but your need can be better served by relying on DB for sorting and a each loop. This avoids the cost of client side sorting and hash manipulations for grouping.
Somewhere in your controller
#all_books = Book.all(:order => "publish_date DESC")
In your view
<%year = nil
#all_books.each do |book|
if year.nil? or year > book.publish_date.year
year = book.publish_date.year
%>
<h1> <%=year%><h1>
<%end % >
<%=link_to book.title + ", (PDF, " + get_file_size(book.size) + ")" %>
<%end %>
The quick and dirty approach is to simply group_by the year and iterate over those:
#all_books.group_by { |b| b.created_at.year }.each do |year, books|
# All books for one year, so put heading here
books.each do |book|
# ...
end
end
This requires sorting within the Rails application, so you will need to retrieve all relevant records in order to have the data properly organized. To do the sort on the server you will probably need to introduce a year column and keep it in sync with the created_at time.

Resources