Cydonia7
Cydonia7

Reputation: 3826

Ruby equivalent for Python's for / else

I've always been searching for something like Python's while / else struct in Ruby to improve my code.

That means that the loop is executed and if the condition in the loop hasn't been true any time, then it returns the value in the else statement.

In ruby, I can do like this :

if @items.empty?
  "Empty"
else
  @items.each do |item|
    item
  end
end

So is there a way to improve this ?

Thank you in advance.

Upvotes: 10

Views: 3644

Answers (4)

Boris Stitnicky
Boris Stitnicky

Reputation: 12578

Since we are in Ruby, let's have fun. Ruby has powerful case construct, which could be used such as this:

case items
when -:empty? then "Empty"
else items.each { |member|
    # do something with each collection member
  }
end

But to make the above code work, we have to modify the native class Symbol first. Modification of native classes is Ruby specialty. This needs to be done only once, typically in a library (gem), and it helps you ever after. In this case, the modification will be:

class Symbol
  def -@
    Object.new
     .define_singleton_method :=== do |o| o.send self end
  end
end

This code overloads the unary minus (-) operator of Symbol class in such way, that saying -:sym returns a new empty object monkey patched with :=== method, that is used behind the scenes by the case statement.

Upvotes: 1

SwiftMango
SwiftMango

Reputation: 15294

A more or less functional way:

empty_action = { true  => proc{ "Empty"}, 
                 false => proc{ |arr| arr.each { |item| item }} }

empty_action[@items.empty?][@items]

Upvotes: 0

scootklein
scootklein

Reputation: 708

Remember that the iterator block returns what you put into it, which can be tested for further use.

if arr.each do |item|
  item.some_action(some_arg)
end.empty?
  else_condition_here
end

Upvotes: 29

Michael Kohl
Michael Kohl

Reputation: 66837

Hm, you could write it as a ternary:

@items.empty? ? 'Empty' : @items.each { |item| item }

You may want to do something more useful in your block though, since each is executed for its side effects and returns the original receiver.

Update as per your comment: I guess the closest you could get is something like

unless @items.empty?
  @items.each { |item| p item }
else
 'Empty'
end

Upvotes: 3

Related Questions