rails - when using Group_by - How to get an Index? - ruby-on-rails

I have the following:
sets = DataSet.all.group_by{ |data| [data.project_id, "-", data.thread_id].join(" ") }
<% sets.each do |range, datas| %>
<p><%= range %>:</p>
<% datas.each do |data| %>
<%=data%>
<p>Last Post<%= data.last.created_at %></p>
<% end %>
<% end %>
Problem is that I need an index. So i updated the above with:
<% sets.each_with_index do |range, datas, i| %>
<p><%= range %>:</p>
<% datas.each do |data| %>
<%= i %>
<%=data%>
<p>Last Post<%= data.last.created_at %></p>
<% end %>
<% end %>
That then breaks, with the error: undefined method `last' for 0:Fixnum
Ideas? thank you

The issue you observe is because of the way parameters are assigned to the block. In your second example, you will observe that range contains an array containing a single range and the matching datas, the datas variable contains the index and i is always nil.
This is because ruby "unsplats" arrays if it is the only parameter to the block. If you have more than one type (in this case an array and an integer), you must hint ruby on what it should do. The simplest way is to use parentheses.
<% sets.each_with_index do |(range, datas), i| %>
...
<% end %>
That way, ruby will know what you mean and split the array up into range and datas. This is actually a feature of ruby's assignment operator in conjunction with the comma operator. It works like this
my_array = [1, 2]
(x, y) = my_array
p x # prints 1
p y # prints 2

Related

Handling previous and successive records within a collection relative to an item in the view of the collection

A collection is defined in a view, where links for each element's successive and previous items need to be generated. (a css-only lightbox. While the index of those items is accessible,
<% #gallery.each_with_index do |article_gallery, index| %>
<%= succ = #gallery[index + 1] %><%= succ.inspect %>
<%= prev = #gallery[index - 1] %>
<% end %>
The inspection of the object returns the expected object
#<ArticleGallery id: 1, article_id: 16, image: "Screen_Shot_2022-11-17_at_07.46.05.png", position: 2, [...]>
But it's id cannot be accessed. if succ.id in lieu of succ.inspect is called it is deemed to now be a nil object.
undefined method `id' for nil:NilClass
#output_buffer.safe_append=' '.freeze;#output_buffer.append=( succ = #gallery[index + 1] );#output_buffer.append=( succ.id );#output_buffer.safe_append='
What is the proper way to access an attribute for the relative previous or successive object?
On the last iteration #gallery[index + 1] will return nil
Try to use https://apidock.com/ruby/Enumerable/each_cons instead of each_with_index
It will be smth like:
<% #gallery.each_cons(2) do |article_gallery| %>
<%= succ = article_gallery[1] %>
<%= prev = article_gallery[0] %>
<% end %>
The answer by Voltan is a partial answer, where it creates a useful array, but as the docs state, the return is nil & that is failing the requirement.
But that was a bit of a lead-in ot the actual solution. The solution was around, but buried a bit deep using ruby's zip method on array.
Given that the impact is on the view, we show the prev and succ id OR (to make the loop continuous) return the last or first element of the array in case the calculated index returns nil
<% #gallery.zip((0 .. (#gallery.size - 1)).to_a).each do |a, i| %>
<%= a.image_url %>
<% prev = #gallery[i-1] %>
<% if prev.nil? %>
<%= #gallery[-1].id %>
<% else %>
<%= prev.id %>
<% end %>
<% succ = #gallery[i+1] %>
<% if succ.nil? %>
<%= #gallery[0].id %>
<% else %>
<%= succ.id %>
<% end %>
<% end %>

Dynamically displaying the column values

Here, I have 10 columns i.e., answer1, answer2, answer3, ..., answer10 in the table MgAnswer.
I have to check whether each column value is present or not. Only if it present,then I have to display it in the page.
Im giving column names dynamically within for loop
<% (1..10).each do |i| %>
<% if MgAnswer."answer#{i}".present? %>
<%= MgAnswer."answer#{i}" %>
<% end %>
<% end %>
Im ending up with Syntax error.
You can indeed dynamically invoke methods in ruby, but this is not the syntax. Instead do
<% (1..10).each do |i| %>
<% if MgAnswer.public_send("answer#{i}").present? %>
<%= MgAnswer.public_send("answer#{i}") %>
<% end %>
<% end %>
It should seem like the following:
<% (1..10).each do |i| %>
<%= MgAnswer.send("answer#{i}") %>
<% end %>
Since ruby can't evaluate line as MgAnswer."method". Also you can just skip if condition, because it will be evaluated to empty string "".

Looping through space separated list in Rails 3

I am using Rails 3.
I got a tags in a column in the database that is saved with space separating each of them like so:
apple orange banana
I want to loop over them and put each on a separate line (between P tags). I got it somewhat running using the code below but the last line outputs the entire string too.
<p>apple</p>
<p>orange</p>
<p>banana</p>
apple orange banana (I do not want this line)
The code I use is this, how can I make it better / replace it so that I do not get the last line when I output the post?
<% item.options.each(' ') do |item| %>
<p><%= item %></p>
<% end %>
<% item.options.split(" ").each do |item| %>
<p><%= item %>
<% end %>
or better yet you might create a virtual attribute in your model:
def tags do
self.options.split(" ")
end
and then
<% item.tags.each do |tag| %>
<p><%= tag %>
<% end %>
<% (item.options.split(' ')).each do |item| %>
<p><%= item %></p>
<% end %>
that should work well

What's the difference between these two ways of using ruby blocks?

This is question is added to my last one.
The problem in my last question was solved by changing the following code:
<p><%= each(2,16,3){|x| x } %></p>
to
<p>
<% each(2,16,3) do |x| %>
<%= x %>
<% end %>
<p>
But I still don't know the difference between the one line style delimited by {} and 3 lines styles by using do and end tag
The first version:
<p><%= each(2,16,3){|x| x } %></p>
takes the return value of the entire each method call and tries to output it. The second version:
<p>
<% each(2,16,3) do |x| %>
<%= x %>
<% end %>
<p>
takes each individual item one at a time and outputs it (since you are evaluating the output inside the block). The actual return value of the each method is not used.
As mentioned by others, this only matters when you need to do some sort of output/calculation inside the block, which each value yielded to the block; the rest is just semantics. The following are the same:
evens = (0..10).to_a.delete_if { |value| value.odd? }
and
evens = (0..10).to_a.delete_if do |value|
value.odd?
end

initializing and incrementing a variable in one line of code

Is this the DRYest way to do it in ruby?
<% for item in #items %>
<%= n = n + 1 rescue n = 1 %>
<% end %>
which initializes "n" to '1" and increments it as the loop progresses (and prints it out) since this is in one my app's views
You can use a ternary operator:
<% for item in #items %>
<%= n = n ? n+1 : 1 %>
<% end %>
But, depending on what you're trying to do, I'm guessing an each_with_index would be more appropriate
<% #items.each_with_index do |item, n| %>
<%= n %>
<% end %>
You could also rely on ruby's nil coercion to an integer which results in zero.
<% for item in #items %>
<%= n = n.to_i + 1 %>
<% end %>
Um.
n = #items.size

Resources