I have the following code that gives me a bunch of locations and an index for each location.
<%= paginate #locations %>
<% #locations.each_with_index do |location, index| %>
<h1><%= index + 1 %></h1>
<h2><%= location.name %></h2>
<% end %>
Everything works fine, I get all the elements in the right order with the right index (palace hast the most visits, then bar, etc.):
1 Palace
2 Bar
3 Cinema
4 Restaurant
5 Ballon
I use Kaminari for pagination and if I klick on the second page to show the next 5 locations, the index starts again at 1, but it should give me 6.
next page (should continue the index with 6)
1 Arena (should be 6)
2 Stadion (should be 7)
and so on ...
So how do I get a persistent counter/index which continues on the next page?
Thanks in advance
Update
I came up with the following, works great so far:
<% if params[:page].nil? || params[:page] == "0" || params[:page] == "1" %>
<% x = 0 %>
<% else %>
<% page = params[:page].to_i - 1 %>
<% x = page * 10 %>
<% end %>
<% #locations.each_with_index do |location, index| %>
<%= index + x + 1 %>
...
each_with_index is a plain ruby method (Enumerable I think) that doesn't know anything about the pagination structure. You may have to provide a additional calculation, unless Kaminari provides something (I haven't use Kaminari yet)
If this were will_paginate, I'd do something like this, and maybe it would work for you too:
<h1><%= index + 1 + ((params[:page] || 0 ) * #items_per_page ) %></h1>
(you have to set #items_per_page yourself)
Related
I have a question about using foreach loops in Ruby.
I want to display documents and am using a foreach loop in order to display these documents. It returns an error with the i variable inside of data["response"]["docs"][i]["topic"] which is a JSON string I am iterating over.
I do not understand why that is. Can anyone tell me what I am doing wrong?
If I simply do data["response"]["docs"][0]["topic"] it works fine but not with the i. Why is that?
<%
(0..10).each do |i|
%>
<%= i %> <br/>
<%= data["response"]["docs"][i]["topic"] %>
<%
end
%>
My question is, how many items are there in data["response"]["docs"]? Are there exactly 11? Either way I would use the following code instead:
<% data["response"]["docs"].each_with_index do |item, index| %>
<%= index %>
<br/>
<%= item["topic"] %>
<% end %>
This iterates over the data["response"]["docs"] no matter how many there are (whether is is 1 doc or 20 docs) and stores the value in the variable named item. The each_with_index function gives you the index as well, stored in index, so you can display it later. If you only want the first 11 use:
<% data["response"]["docs"].first(11).each_with_index do |item, index| %>
This will grab a maximum of 11 doc items.
It's hard to tell what might be going wrong because you haven't posted the error, but if you're using a 10-element array, you want to do:
(0..9).each do |i|
With 0-based indexes, you should only use the range from 0-9, rather than 0-10. You may be getting an error because you're trying to access an element that isn't there (i.e. at index 10).
Even better is:
<% data["response"]["docs"].each do |document| %>
<%= document["topic"] %>
<% end %>
or if you need to print the index:
<% data["response"]["docs"].each_with_index do |document, index| %>
<%= index %> <br/>
<%= document["topic"] %>
<% end %>
I've created a hash through a loop that looks like this.
<% ship_hash = {} %>
<% #order_preview.fedex_rates.each do |rate| %>
<% if rate.service_name == "FedEx Ground Home Delivery" || rate.service_name == "FedEx 2 Day" || rate.service_name == "FedEx Standard Overnight" %>
<% ship_hash["#{rate.service_name}"] = "#{number_to_currency(rate.price.to_f / 100)}" %>
<% end %>
<% end %>
<% #order_preview.usps_rates.each do |rate| %>
<% if rate.service_name == "USPS Priority Mail 1-Day" %>
<% ship_hash["#{rate.service_name}"] = "#{number_to_currency(rate.price.to_f / 100)}" %>
<% end %>
<% end %>
What I am trying to do is simple. I want to get the output of the keys and values through a block.
>> ship_hash
=> {"FedEx Ground Home Delivery"=>"$9.78", "FedEx 2 Day"=>"$20.59", "FedEx Standard Overnight"=>"$33.78", "USPS Priority Mail 1-Day"=>"$5.60"}
Ok, that's what I expected...
>> ship_hash.each { |key, value| puts "#{key}: #{value}" }
I get
=> {"FedEx Ground Home Delivery"=>"$9.78", "FedEx 2 Day"=>"$20.59", "FedEx Standard Overnight"=>"$33.78", "USPS Priority Mail 1-Day"=>"$5.60"}
When I thought I would get something like this
FedEx Ground Home Delivery: $9.78...
On the RubyMonk primer page here is why I am confused. I don't get the output I expect!!
I am running the commands from better_errors after I <%=raise %> after the code generates hash.
I must be missing something, this is basic and unexpected... Feel free to suggest alternate titles to help future confundos ...
Update
Well it sounds like a live shell isn't the place to run these commands, as it only returns the hash itself. What I am trying to do in practicality is populate a f.select field with both the key and value of the hashes. Not to change the question too much, but How could I populate the option of a select field so it displays the key and values next to eachother?
If you go in irb (or rails console) and type the following:
ship_hash = {"FedEx Ground Home Delivery"=>"$9.78", "FedEx 2 Day"=>"$20.59", "FedEx Standard Overnight"=>"$33.78", "USPS Priority Mail 1-Day"=>"$5.60"}
ship_hash.each { |key, value| puts "#{key}: #{value}" }
You'll see the following output:
FedEx Ground Home Delivery: $9.78
FedEx 2 Day: $20.59
FedEx Standard Overnight: $33.78
USPS Priority Mail 1-Day: $5.60
=> {"FedEx Ground Home Delivery"=>"$9.78", "FedEx 2 Day"=>"$20.59", "FedEx Standard Overnight"=>"$33.78", "USPS Priority Mail 1-Day"=>"$5.60"}
What's happening is that ruby is printing to the console the expected output, and then returning the hash itself as the return value.
So you should see the output that you expected in your rails logs. However, the html being displayed is the return value of your each statement, which is just the hash itself.
What you want to do is something like this:
<ul>
<% ship_hash.each do |key, value| %>
<li><%= key %>: <%= value %></li>
<% end %>
</ul>
This will output the key value pairs of the hash in the form of an unordered list.
My app is reaching a point where I must begin optimizing for performance. I've posted some code from my view that I feel can be improved.
In the view, I am treating the first item in the index a certain way and the rest of the items another way. Each time a new item is iterated over, it is being checked (Ruby asks itself.. does this item have an index of 0?)
I feel like performance can be improved if I can stop that behavior by treating the first item special with index.first? and treating the other items another way (without even checking whether they have an index of zero) How can this be done?
<% #links.each_with_index do |link, index| %>
<% if link.points == 0 then #points = "?" else #points = link.points %>
<% end %>
<% if index == 0 then %>
<h1> First Item </h1>
<% else %>
<h1> Everything else </h1>
<% end %>
<% end %>
<% end %>
You can do this non-destructively like so:
first, *rest = *my_array
# Do something with 'first'
rest.each{ |item| … }
…where first will be the first element (or nil if my_array was empty) and rest will always be an array (possibly empty).
You can get the same results more easily if it's OK to modify your array:
# remove the first item from the array and return it
first = my_array.shift
# do something with 'first'
my_array.each{ |item| … }
However, this will only clean up your code; it will make no measurable performance difference.
Say I have a block like this:
<% #help_sections.each do |section| %>
<li><%= section.name %></li>
<% end %>
But on the last record returned, I want to do something else, e.g. applying a class to the li that's there:
<li class="last"><%= section.name %></li>
How do I do that in the most DRY way?
Thanks.
Edit1:
I imagine I would simply use an if statement and the last ruby method, but not sure how to do that within the block? I know that if I just wanted the last element in that array, I could just do #help_sections.last, but that doesn't make sense within the confines of a Ruby block.
The most DRY way is to use CSS instead. Instead of e.g. this:
li.last { color: red; }
..and then cluttering up your markup with an extra CSS class, just use the :last-child pseudoselector, i.e.:
li:last-child { color: red; }
Then you don't have to change anything in your view. This is supported in all modern browsers including IE9.
Try each_with_index:
<% #help_sections.each do |section, index| %>
<li <%= "class='last'" if index == (#help_sections.length-1) %>><%= section.name %></li>
<% end %>
DRY is a good idea in general, but don't kill yourself to keep from repeating a li.
<% #help_sections[0...-1].each do |section| %>
<li><%= section.name %></li>
<% end %>
<li class="last"><%= #help_sections.last.name %></li>
You could either do something like
<% #help_sections.count.times do |i| %>
<%= #help_sections[i].name %>
<%= do_something if #help_sections.count == i - 1 %>
<% end %>
This old answer might help (Tell the end of a .each loop in ruby). Basically, you can use:
<% #help_sections.each_with_index do |section, index| %>
<% if index == #help_sections.size - 1 %>
<li class="last">
<% else %>
<li>
<% end %>
<%= section.name %></li>
<% end %>
If you use each_with_index instead of plain each, the block will also be passed the index of the current element within the collection. You can then compare that to #help_sections.length.
E.g.
<% #help_sections.each_with_index do |section, i| %>
<li<% concat " class='last'" if i == #help_sections.length - 1 %>><%= section.name %></li>
<% end %>
In general cases not covered by smart CSS selectors, you can define a convenient helper method, which would provide each iterated element with its context within the collection. Like this one:
def each_with_context enum
length = enum.count
enum.each_with_index do |elem, i|
context = {
:first => i == 0,
:last => i == length - 1,
:even => i.even?,
:odd => i.odd?,
:middle => i == length / 2
}
yield elem, context
end
end
And then use it within HAML view like this:
-each_with_context(#help_sections) do |section, context|
%li{:class => context[:last] ? 'last' : nil}
=section.name
I have a controller with an API request showing all my Google Docs.
feed = client.get('http://docs.google.com/feeds/documents/private/full').to_xml
feed.elements.each('entry') do |entry|
puts 'title: ' + entry.elements['title'].text
puts 'type: ' + entry.elements['category'].attribute('label').value
puts 'updated: ' + entry.elements['updated'].text
puts 'id: ' + entry.elements['id'].text
# Extract the href value from each <atom:link>
links = {}
entry.elements.each('link') do |link|
links[link.attribute('rel').value] = link.attribute('href').value
end
puts links.to_s
end
So, I can see the results in my console but how do I get them into my view?
I tried with something like this, but that doesn't work (I changed my variable in the controller to an accessor of course)
<% feed.elements.each('entry') do
|entry| %> <%
entry.elements['title'].text %> <%
end %>
First, in your controller, make feed an instance variable. IE: it should be:
#feed = client.get..... instead of feed = client.get....
If that doesn't fix it... I don't know your API for sure, but I suspect you may need to be using:
<% #feed.elements.each('entry') do |entry| %> <% entry['title'] %> <% end %>
Note: entry['title'] instead of entry.elements['title'].text
What your current code indicates is that the feed is structured like this:
feed.elements[0].elements['attr'].text, when it's probably just feed.elements[0]['attr']
Does that make sense? Try that and see what happens.
If that doesn't work, just put: debug(#feed) in your view and copy and paste it to the end of your question. That'll help us figure out the right way to access this info.
Problem solved. Because I use 'puts' in the controller to show the content of the feed in the console I also have to change that for the view. Of course, puts is equal to <%= ... %>.
<ul>
<% #feed.elements.each('entry') do |entry| %>
<li><%= 'title: ' + entry.elements['title'].text %></li>
<% end %>
</ul>