Limit each do loop - ruby-on-rails

If I have the following,
<% #feed.sort_by{|t| - t.created_at.to_i}.each do |feed| %>
<% end %>
How can limit it to only show the 10 most recent results

<% #feed.sort_by{|t| - t.created_at.to_i}.first(10).each do |feed| %>
However, it's probably best to push this down into the model like this
<% #feed.recent(10).each do |feed| %>
And, in fact, if #feed comes out of a database, I'd push it down even further: it doesn't make sense to load a ton of unsorted feed entries out of the DB, then sort them and then throw most of them away. Better let the DB do the sorting and filtering.
See #Peer Allan's answer for how to do it in ActiveRecord. In ARel (IOW: Rails 3) it would probably be even simpler, something like
Feed.all.order('created_at DESC').take(10)

Array#first(n)
[1,2,3,4,5].first(3)
=> [1,2,3]

I'd do it like this:
<% #array.limit(10).each do |a| %>

I agree with the others (Jörg in particular); but if you still want to know how to limit the loop itself, break can be useful.
#array.each_with_index do |feed, i|
break if i == 10;
# ...

The following code will return 10 recent records.
#feed = #feed.sort! { |a,b| b.created_at <=> a.created_at }.take(10)
Array Reference

The created_at seems to indicate that you are using ActiveRecord in Rails to get set the #feed variable. If that is the case you are better to do this work in SQL. Its far more efficient and easier to deal with.
#feed = Feed.all(:order => 'created_at DESC', :limit => 10)
Otherwise if you really want to use the view to do this you can use first or a range
<% #feed.sort_by{|t| - t.created_at.to_i}[0..9].each do |feed| %>
<% #feed.sort_by{|t| - t.created_at.to_i}.first(10).each do |feed| %>

Related

How do I elegantly check for presence of both the object and associated objects?

I have an instance variable #tally_property, and if there are photos on that object I would like to cycle through the photos and show them.
So my code snippet looks like this:
<% if #tally_property.photos.present? %>
<% #tally_property.photos.each_with_index do |photo, index| %>
The issue is that based on the above, if #tally_property is nil, then the entire first line throws an error.
So is there a 'nil' check I can do that isn't bulky, i.e. I don't want to do if #tally_property.nil?, on both the primary object and the association, and is elegant and ruby & rails-esque?
I would use the safe navigation operator (&.) and write something like this:
<% #tally_property&.photos&.each_with_index do |photo, index| %>
...
<% end %>
In Ruby 2.3.0+ you can use the safe navigation operator:
#tally_property&.photos
ActiveSupport has a .try method that can be used to the same end in older versions of ruby:
#tally_property.try(:photos)
You can add a simple conditional to be able to safely iterate through the collection:
<% (#tally_property.try(:photos)||[]).each_with_index do |photo, index| %>
<% end %>
Rails 4 adds ActiveRecord::Relation#none and a change in behaviour so that associations always return a ActiveRecord::Relation. So its perfectly acceptable to write:
<% #tally_property.try(:photos).try(:each_with_index) do |photo, index| %>
<% end %>
After upgrading your app. Or you can use a partial and render:
<%= render partial: 'photos', collection: #tally_property.photos if #tally_property %>
Which removes the need for writing the iteration.
Use && (or and, they each have their sweetspot).
Taking it out of Erb for a moment, I would generally write something like this:
if #tally_property and #tally_property.photos.present?
Depending on photos I might use:
if #tally_property and #tally_property.photos
or perhaps:
if #tally_property and not #tally_property.photos.empty?
Sometimes I'll use a temporary variable:
if (photos = #tally_property && #tally_property.photos)
photos.each #…
That kind of thing.
I would recommend this episode of Ruby Tapas, And/Or for a longer (but still quick) look at it.
One more way, just select all photos connected to this tally_property:
example how it might be:
Photo.joins(:tally_property).each_with_index do |photo, index|

How to select 6 random records from table

My portfolio_controller.rb has an index method like this:
def index
#portfolio = PortfolioItem.all
end
How can I specify in the condition that the code in this block should be executed 6 times? In other words, how can I access exactly 6 values from the #portfolio object in my view, using a loop? This is what I have so far:
<% #portfolio.shuffle.each do |portfo| %>
Using all, followed by shuffle, is a bad solution for two reasons.
A slight improvement would be to use sample(6) instead of shuffle.first(6), as this removes a step from the process.
However, the bigger issue here is that Portfolio.all.<something> (where the <something> method requires converting the data into a ruby Array) will fetch all of the data into memory - which is a bad idea. As the table grows, this will become a bigger performance issue.
A better idea is to perform the "random selection" in SQL (with the order and limit methods), rather than in ruby. This avoids the need to fetch other data into memory.
The exact solution is database-specific, unfortunately. For PostgreSQL and SQLite, use:
Portfolio.order('RANDOM()').limit(6).each do |portfolio|
Or for MySQL, use:
Portfolio.order('RAND()').limit(6).each do |portfolio|
You could define this as a helper in the Portfolio model - for example:
class Portfolio < ApplicationRecord
# ...
scope :random_sample, ->(n) { order('RANDOM()').limit(n) }
# ...
end
And then in your view:
#portfolio.random_sample(6).each do |portfolio|
You can something like this :
<%(1..6).each do |i| %>
<% #your statements %>
<%end%>
<% #portfolio.shuffle.each_with_index do |portfo, index| %>
<%if index <= 6%>
<p><%= portfo.title %></p>
<%end%>
<% end %>
Or You can do it as
<% #portfolio.shuffle.take(6).each do |portfo| %>
<p><%= portfo.title %></p>
<% end %>

How can I limit the number of records to load in view???

This code shows all the records of CommunityTopic that belongs to current Community.
How can I limit the numbers to 10 records to display here?
<ul>
<% #community.community_topics.each do |topic| %>
<li>
<%= link_to topic.title, community_topic_path(#community, topic) %>
<%= link_to topic.user.user_profile.nickname, community_topic_path(#community, topic) %>
</li>
<% end %>
</ul>
Use the limit method:
<% #community.community_topics.limit(10).each do |topic| %>
This will only supply the first 10 elements of the collection to the block. If you want to be more sophisticated, you could use something like will_paginate.
In general, such data fetching should take place in the controller. So instead of having a #community variable where the view gets the data from, have a #community_topics as well, which is prefilled with the data you want to render.
You shouldn't usually do this in the view, but rather in the controller. You can use limit as #Fermaref proposed, or you can use a paginator to help you out such as will_paginate or kaminari.
To move this to the controller, try something like this:
def some_action
#community = Community.find(params[:id])
#community_topics = #community.community_topics.order(:some_attribute).limit(10)
end
Then just use #community_topics in your view. One advantage here is that you can now move this logic to a private method for reuse if needed. You can also functionally test that #community_topics limits to 10 rows.
Use 8.3 reorder
The reorder method overrides the default scope order. For example:
#categories = Category.includes(:subcategories).where(active: true).references(:subcategories).order(name: :asc).reorder('categories.name ASC', 'subcategories.name ASC')
http://guides.rubyonrails.org/active_record_querying.html

how to delete/update several instances with one form in rails

I've just started programming in rails 3 days ago, learned ruby by the same time, and I'm having a hard time since yesterday figuring how to with one form, delete/update some of my instances, I only use one model "Task". Here's the code:
<%= form_for #task do |f| %>
<ul>
<% #tasks.each do |task| %>
<li id="task"><%= f.check_box :done %> <%= f.label :name %> </li>
<% end %>
<button onclick="doUpdate()">Mark Selected as done </button>
<%= button_to "Delete selected", :method => :delete %>
</ul>
<% end %>
Here's the controller:
def delete
#tasks = Task.find(:all, :conditions => ["task.done = ?", true])
#tasks.each do |task|
task.delete
end
#tasks = Task.all
end
My model have only 2 parameters. name:String and done:Boolean, I wan't to delete all the selected checkboxes. But this don't work for me
Thanks in advance.
The problem is, you are doing it wrong(and I'll tell you why). I could paste the code that would make it work but I'd rather explain, as you are probably doing it to learn.
Task.find(:all, :conditions => ["done = ?", true]) will return EVERYTHING in your database where done = true. You will be erasing everything that is marked as done in the DATABASE, not what were marked on the form. Task is your model, you can access the database by using find, where and other methods from activerecord(if activerecord doesn't sound natural to you, activerecord lets you get stuff from the database without the need of writing SQL queries).
What you really need to do in your controller is:
- You have to get what was sent from the form (check the documentation/web resources for the usage of param[] ).
- For every checkbox marked true, you erase a record. (you got the each part right, this is good!)
I don't think your view is right, I advise you to first be sure that the data that you receive is right(the params[]), then proceed to try to erase the record, or do whatever you want to do with it.
To "test" if your variables and code that is inside your controllers and models, use print #variable or something else(check rails docs how to debug).
I advise you use destroy instead of delete as other fellow stackoverflowers have said. Read the docs of destroy and delete.
Keep going :)
On a first glance, try using task.destroy instead of task.delete, and done instead of task.done See delete vs. destroy.
def destroy
#tasks = Task.find(:all, :conditions => ["done = ?", true])
#tasks.each do |task|
task.destroy
end
#tasks = Task.all
end

Rails: display #cars as a comma-separated list

Based on this query:
#cars = Car.where("manufacturer_id IN ?", #mfts.select("id")).limit(30).select("id")
How can I display the cars' IDs in the view like this (or do I need to rewrite my query)?
3,2,5,12,15,24,34,63,64,65,66,85
Thanks a lot - I've looked for this but couldn't find the right question/answer.
One solution is to do:
#view
<% #cars.each do |c| %><%= c.id %>,<% end %>
I don't know if there's a better way to go about it - this obviously leaves a stray comma at the end of the list (which isn't a dealbreaker). Any more elegant solutions?
One line:
<%= #cars.map(&:id).join(",") %>
If writing &:id seems confusing, there's another way that's a little more readable.. If y'all want to access a method or attribute, it might look better to inline a block.
<%= #cars.map { |car| car.id }.join(", ") %>
P.S... another name for map is collect.. that's what it's called in Smalltalk.
Lookin' good!
With Rails 3.0+ you can now write:
<%= #cars.map { |car| car.id }.to_sentence %>
Rails will appropriately add the comments and the word 'and' between the last two elements.

Resources