I love each
, but I have a problem with it. What happens when you have an empty collection?
If you call [].each
, nothing will happen and []
will be returned. Sometimes, that’s what you want. But more often, especially when you’re building UI, you’ll want to handle empty lists in a special way. For example, I usually want to show a different message when I don’t have any data.
But since [].each
returns []
, not nil
, you’re stuck writing something like this:
<% if @users.empty? -%>
<p>You don't have any users. <%= link_to "Add one!", new_user_path %></p>
<% else -%>
<% @users.each do |user| -%>
<p><%= user.name %></p>
<% end -%>
<% end -%>
That works, but it feels awkward. I’d rather say, “Do this thing to each item in this collection, unless there’s nothing in it, in which case do this other thing entirely.” I want something like each
, with an “or else.”
Rendering Rails Collections
Inside your views, Rails can help. It’s great at rendering collections of things:
<%= render @users %>
<p><%= user.name %></p>
When render
is passed @users
, it renders _user.html.erb
once for each user inside @users
. You can skip the each
entirely.
As a bonus, if @users
is empty, render
returns nil
, just like we want! So you can write this, and get the same output as the original version:
<%= render(@users) || render('empty') %>
<p><%= user.name %></p>
<p>You don't have any users. <%= link_to "Add one!", new_user_path %></p>
It’s a lot more direct, once you understand Rails’ conventions.
What about outside of a view?
If you follow Rails, render
with a collection is a fast, powerful way to render collections of objects.
But sometimes you won’t want to deal with an extra partial to render each item. Or render
won’t support your design. Or you won’t even be inside a view.
The best solution I’ve found so far is presence
.
list.presence
is the same as:
if list.present?
list
else
nil
end
That is, you’ll get the list back if it has anything in it, and nil
if it’s empty.
With presence
, you could write:
@users.each do |user|
puts user.name
end.presence || puts("You don't have any users.")
This prints each name if there are users in @users
, or You don't have any users
otherwise.
Still, I could do without the presence
. It feels like a hack, because it is one. If it was supported, something like this might be better:
@users.each do |user|
puts user.name
end || puts("You don't have any users.")
or the Smalltalk-ish:
@users.each(if_empty: lambda { puts "You don't have any users." }) do |user|
puts user.name
end
or even (gasp!):
for user in @users
puts user.name
else
puts "You don't have any users."
end
For now, though, I usually just go with presence
or the basic if blank?; else each
pattern.
Which do you think is better? How do you handle empty lists? Have you found a better way? If so, tell me about it!