What I am hoping to do is create a timeline of user events pulled from multiple differnt tables that are assosiated with the user.
For example
#user.notifications
#user.followings
#user.enrollments
I don't know if there is a way to loop through this information sorted by created_at.
like
<% (#user.notifications) + (#user.followings) + (#user.enrollments).each do |n, f, e| %>
Any guidence would be super appreciated!
You are correctly adding the arrays together (you don't actually need the parans around each element, but you need to wrap the entire adding statement in parans) so now you just need to sort them.
Try this:
<% (#user.notifications + #user.followings + #user.enrollments).sort_by(&:created_at).each do |event| %>
Related
Having a challenge to calculate sum for dynamically built request.
I'm using each method to get value for each element and it work seamless.
<% params[:car].map{|n| n.first}.each do |p|%>
<%= #salon.price.send("price_" + p) %>
<% end %>
But then I'm trying to get sum for the same dynamically ("price_" + p) built queries it's failing.
<%= #salon.price.where("price_" + params[:car].map{|n| n.first}.to_s).all %>
Tried multiple solutions and no luck
You have where but haven't given it an actual where-like clause do you mean #salon.price.sum() instead? Otherwise what are you trying to filter on (where is for filtering, sum is for summation).
So what you seem to want to do is:
for all the prices for a given salon
sum up the columns price_0..price_n
right?
Now it'd be easy to construct a query to sum up the values for a single column
For that you'd try something like this:
<%= #salon.price.sum("price_0") %>
This uses the Rails sum method that works on any Active Record association.
And if you had a single price object and wanted to sum up all the price_X columns for that single price, you'd use something like this:
<%= params[:car].map{|n| price.send("price_" + n.first.to_s) }.sum %>
This turns the numbers in params[:car] into an array of the column-values for the given price-object... then sums them at the end using the sum method that comes from the Array class (AKA Array#sum)
so to combine those two, you'd probably need something like this:
<%= #salon.prices.all.sum{|price| params[:car].map{|n| price.send("price_" + n.first.to_s) }.sum } %>
Yes, that's a block inside a block.
Now it's possible that the Active Record version of sum can interfere with Array#sum and not like the above (which looks more like how you'd do the Array#sum). Rails' sum expects you to pass it the name of a single column like in the first example, rather than a block, like in the second example. So sometimes you then need to use map (which turns your set of values into an array) and then you can use Array#sum at the end like this:
<%= #salon.prices.all.map{|price| params[:car].map{|n| price.send("price_" + n.first.to_s) }.sum }.sum %>
So that's a block (whose values are summed) inside another block (whose values are summed)
EDIT:
after discussion it seems you only have a single price record... and multiple columns on that single record... this changes things and makes them much simpler. You can just use this:
<%= params[:car].map{|n| #salon.price.send("price_" + n.first.to_s) }.sum %>
You use sum method for the array
sum = params[:car].map{|n| n.first}.sum
I'm guessing you're trying to get the sum of columns named price_n in the Price table where n is the value of params[:car].map(:&first). So I think the simple solution is:
<% params[:car].map(&first).each do |n| %>
<% sum += #salon.price.send("price_#{n.to_s") %>
<% end %>
<%= sum %>
But seeing the logic in the view is not a rails best practice, so it's better if we move the entire logic in your helper method. So in the view, just display this code:
<%= total_of_all_prices(params[:car], #salon.price) %>
Then in your helper method add this method
def total_of_all_prices(car_params, price_object)
sum = 0
car_params.map(&:first).each do |n|
sum += price_object.send("price_#{n.to_s}")
end
sum
end
I'm trying to find the last Econ_Result that belongs to a Econ_Report. I want to display the last record of the Econ_Result (ordered by "release_date") for each Econ_Report on the index view. In the controller I tried to take the list of all reports and find the last result using the following:
#econ_reports = EconReport.all
if #econ_reports.econ_results.size >= 1
#last_result = #econ_report.econ_results.last.release_date
end
econ_report.econ_results.size works on the index view when I place it in for each loop. When I try to call the value of the last record I run into issues with the fact that some reports don't yet have results (a temporary issue) so I threw in the if then check in the controller which is currently failing.
Thanks in advance for the rookie help.
Since #econ_reports is a collection of EconReport objects, you can't call an instance method like .econ_results on it. Instead, you can only call it on instances within the collection:
#econ_reports.each do |econ_report|
if econ_report.econ_results.any?
last_result = econ_report.econ_results.last
end
end
However, this can be terribly inefficient for a large collection of #econ_reports: both lines with econ_report.econ_results will query the database separately, meaning that you'll query the database independently for each econ_report in the collection. This is known as the N+1 query problem.
Luckily for you, as discussed in the link, Rails has a built-in solution to optimize this code so you'll only query the database once:
<% #econ_reports.includes(:econ_results).each do |econ_report| %>
<% if econ_report.econ_results.any? %>
<% last_result = econ_report.econ_results.last %>
# do something to display last_result
<% end %>
<% end %>
If you just want the release date you might try:
#last_result = #econ_report.econ_results.order('release_date DESC').limit(1).pluck(:release_date).first
It's worth noting that a Ruby if statement generally looks like:
if condition
end
The then is almost always omitted even though it is allowed.
In an .html.erb file, it is quite natural to write something like this:
<% unless #results.empty? %>
<ul>
<% #results.each do |result| %>
<li>
<%= link_to result.name, '#' %>
</li>
<% end %>
</ul>
<% end %>
Where #results is the result of an ActiveRecord .all query. Unfortunately this generates two queries to the database: the first looking for a count of the results (the unless condition), the second to retrieve actual results. In this case the query is particularly expensive.
I could simply convert results to an array (which would buffer the entire result set), or put complex logic in the .erb
Neither solution seems to fit the Rails/ActiveRecord design philosophy.
Is there a better way to eliminate the duplicate query?
Rails tries to be smart and not load a whole association/relation when it doesn't need it. As a result some methods on relations or associations look like their counterpart from Enumerable but will instead run some sql if the association is not loaded. first, any?, include? are examples of this.
The easiest way, when you know that this is a case when this optimisation is not paying off is to force the relation to be loaded. You could do this by converting to an array to_a but you might as well be more direct.
#results = Foo.where(...).load
This is also keeps #results as a relation rather than converting to an array.
I'm learning rails and have been trying to read through documentation, but I just don't get how #group
works.
The documentation says that it: "Returns an array with distinct records based on the group attribute".
How do you then retrieve the records that belong to a certain group?
Say I want to group Articles by the month in which they were created? How would I do that?
The group method is generally used with the select method to do aggregating queries. For instance, if you wanted to count your articles by month, you could do this:
by_month = Article.group(:month).select("month", "COUNT(*) as count")
In this case, COUNT is the SQL aggregate function that counts rows, and we're putting the count result into an output column called "count".
Note: This assumes you have a column called "month". Of course you can do SQL here, so you might have, e.g. MONTH(created_at) instead, or whatever makes sense in your case.
You could then do this to output the month and its associated article count:
by_month.each do |row|
puts "Month #{row.month}: #{row.count}"
end
This probably seems mysterious because your model has no column "count", but that's the way select works: It defines new output columns for the query on the fly, and ActiveRecord happily maps those for you in the resulting instance objects.
This kind of query is dramatically more efficient than loading all the records and counting them yourself because you're letting the database do the heavy data work, and that's what it's good at.
It is perfectly legal to use group without select but the result is not usually what you want. If you group your articles by month, you'll get one object in the result for each month. The columns available in each object vary by database back end, but in MySQL they will have the values from the "first" row encountered for each group. If you aren't sorting, "first" is essentially undefined.
If by "group Articles by the month in which they were created" you mean you want this kind of grouping on a web page result, then you'll have to do it yourself, e.g.:
<% last_month = nil %>
<% #articles.each do |article| %>
<% if last_month != article.month %>
<h2><%= article.month %></h2>
<% last_month = article.month %>
<% end %>
# [output the article]
<% end %>
If you do something like this, you'll need to be sure #articles is ordered by month.
My way
controller pattern 1 (note: Here, it's calling all users!!)
#users = User.confirmed.joins(:profile)
view pattern 1 (note: Here, it only shows first 10 users but it show the number of all users!!)
<%= "ALL ("+ #users.count.to_s + " users)" %>
<% #users.limit(10).each do |users| %>
<%= render 'users/user', :user => users %>
<% end %>
Should it be just like this below if I'm considering page loading speed?
Or it won't be changed?
controller pattern 2 (note: I added limit(10), and #users_count to count all users)
#users = User.confirmed.joins(:profile).limit(10)
#users_count = User.confirmed.joins(:profile).count
view pattern 2 (note: I took it off limit(10) and use #users_count for count)
<%= "ALL ("+ #users_count.to_s + " users)" %>
<% #users.each do |users| %>
<%= render 'users/user', :user => users %>
<% end %>
If you have lazy loading disabled, then the second approach would be faster because Rails doesn't need to fetch all records from the database. You should really fetch only the records you need when performing queries.
If you have lazy loading enabled (by default), then it is the same, because the data is fetched when it is needed, so the effect will be the same. You can also put two variables in controller and write the same query as you did in the view and the data will be fetched only if and when it is needed.
#users = User.confirmed.joins(:profile)
#users_count = #users.count
#users = #users.limit(10)
You can check sql generated by the app in your rails console and then decide.
Also, if you are using profile in user.html.erb, consider using includes instead of join. Join can cause n+1 problem if you need associated records. If you don't, you do not want to fetch records you don't need. You can read more about it here, in 12 Eager Loading Associations.
The two options are exactly the same. Neither of them loads all the Users because you're just chaining scopes. The query is only run when you call .each in the view, at which point you've applied the .limit(10) anyway. I'd go with the first option because the code is cleaner.
#users.count does one query to get the count, it doesn't instantiate any User objects.
#users.limit(10).each ... does one query (actually two because you've used includes) with a limit, so it will instantiate 10 objects plus your includes.
you can try #users.find_in_batches
Please take a look
Find in batches
Please let me know
If you want speed loading
I can suggest you memcache Memcache