24

1.I can't find an elegant way to write this code:

if array.empty?
  # process empty array
else
  array.each do |el|
    # process el
  end
end

I'd like to have one loop, without writing array twice. I read this, but there is no solution good enough.


2. I am actually in an HAML template. Same question.

- if array.empty?
  %p No result
- else
  %ul
  - array.each do |el|
    %li el
sawa
  • 165,429
  • 45
  • 277
  • 381
Augustin Riedinger
  • 20,909
  • 29
  • 133
  • 206
  • 1
    The fact that you're in a HAML template is actually the key piece of info here that makes this more than just a Ruby question. In plain Ruby, you'd just loop and process. But because you're at the view layer, you're generating presentation data dictated by a data structure. I'm sure there is some view methodology that you can use to simplify it somehow. But meanwhile I'd just lose the 'else' and make two distinct contexts. But understand that this distinction is beyond just Ruby. – danneu May 31 '13 at 15:37
  • What do you do inside the processing empty array part? – Shoe May 31 '13 at 15:43
  • I do understand that the HAML is the key here, but if Ruby had had the proper operator, then it would have been much easier, right? I'm not sure I understand what you mean though. How'd you make 2 distinct contexts? – Augustin Riedinger May 31 '13 at 22:23

7 Answers7

37

What about?

array.each do |x|
  #...
  puts "x",x
end.empty? and begin
  puts "empty!"
end
vitas
  • 598
  • 5
  • 9
  • I want to do this with array.each_slice but it doesn't work. Do you know if there's some equivalent?. Thanks. – Luis Crespo Oct 02 '14 at 23:14
  • 3
    Oh my god, this is why I love Ruby! – David Routen Jan 19 '16 at 16:41
  • Is there a way to extend this to *also* handle the `.nil?` case separately? – sschuberth Feb 16 '17 at 09:21
  • Damn, been using Ruby for nearly 15 years and this is the first time I'm seeing this. It's a little tricky to see in the code but boy is it clean. Thanks. I'll try this out for a bit and see if it works well and becomes our new standard way of dealing with empty `Array`s and `ActiveRecord::Relation`s. – Joshua Pinter Jan 11 '23 at 20:05
6

The cleanest way I've seen this done in HAML (not plain Ruby) is something like:

- array.each do |item|
    %li
        = item.name
- if array.empty?
    %li.empty
        Nothing here.

As mentioned by other answers, there is no need for the else clause because that's already implied in the other logic.

Even if you could do the each-else in one clean line, you wouldn't be able to achieve the markup you're trying to achieve (<p> if array.empty?, <ul> if array.present?). Besides, the HAML you show in your question is the best way to tell the story behind your code, which means it will be more readable and maintainable to other developers, so I don't know why you would want to refactor into something more cryptic.

colllin
  • 9,442
  • 9
  • 49
  • 65
2

I think there is no much more elegant or readable way to write this. Any way to somehow combine an iteration with a condition will just result in blackboxed code, meaning: the condition will just most likely be hidden in an Array extension.

Beat Richartz
  • 9,474
  • 1
  • 33
  • 50
2

If array is empty, then it will not be iterated, so the each block does not need to be conditioned. Since the return value of each is the receiver, you can put the each block within the empty? condition.

if (array.each do |el|
  # process el
end).empty?
  # process empty array
end
sawa
  • 165,429
  • 45
  • 277
  • 381
1

Assuming that "process empty array" leaves it empty after processing, you can leave out the else:

if array.empty?
  # process empty array 
end
array.each do |el|
  # process el
end

or in one line:

array.empty? ? process_empty_array : array.each { |el| process_el } 
Matt Gibson
  • 14,616
  • 7
  • 47
  • 79
0

An if the array is nil then we can enforce to empty array

if (array || []).each do |x|
  #...
  puts "x",x
end.empty?
  puts "empty!"
end
mmsilviu
  • 1,211
  • 15
  • 25
-1

I saw some people asking how to handle this for nil cases.

The trick is to convert it to string. All nils converted to string becomes a empty string, all empty cases continue being empty.

nil.to_s.empty?
"".to_s.empty?

both will return true

Diogo Publio
  • 9
  • 1
  • 2