Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: nested if statements

Tags:

ruby

nested-if

I was writing some code and it ended up being way too ugly to my liking. Is there anyway that I can refactor it so that I don't use nested if statements?

def hours_occupied(date)
  #assuming date is a valid date object    
  availability = get_work_hours(date)
  focus = "work"

  if availability.nil
    availability = get_family_hours(date)
    focus = "family"

    if availability.nil
      availability = get_friend_hours(date)
      focus = "friends"
    end
  end
end

I know I'll be able to do something like this for availability

availability = get_work_hours(date) || get_family_hours(date) || get_friend_hours(date)

but how do I set the focus variable accordingly?

like image 679
rlhh Avatar asked Jun 19 '13 14:06

rlhh


People also ask

How do you write or condition in Ruby?

In Ruby, “or” keyword returns the logical disjunction of its two operands. The condition becomes true if both the operands are true. It returns “true” when any one condition/expression is “true” and returns “false” only when all of them are “false”.

What does unless mean in Ruby?

Ruby provides a special statement which is referred as unless statement. This statement is executed when the given condition is false. It is opposite of if statement.


4 Answers

I would do something like the following as it makes it clear that each case is mutually exclusive:

def hours_occupied(date)
  if availability = get_work_hours(date)
    focus = "work"
  elsif availability = get_family_hours(date)
    focus = "family"
  elsif availability = get_friend_hours(date)
    focus = "friends"
  end
end
like image 174
Don Cruickshank Avatar answered Oct 15 '22 01:10

Don Cruickshank


I'd write:

def hours_occupied(date)
  focus = if (availability = get_work_hours(date))
    "work"
  elsif (availability = get_family_hours(date))
    "family"
  elsif (availability = get_friend_hours(date))
    "friends"
  end
  # I guess there is more code here that uses availability and focus.
end

However, I am not sure having different methods for different types is a good idea, it makes code harder to write. A different approach using Enumerable#map_detect:

focus, availability = [:work, :family, :friends].map_detect do |type|
  availability = get_hours(date, type)
  availability ? [type, availability] : nil
end
like image 32
tokland Avatar answered Oct 15 '22 01:10

tokland


One more way is just to reassign values if there is a need:

def hours_occupied(date)
  availability, focus = get_work_hours(date), "work"
  availability, focus = get_family_hours(date), "family" unless availability
  availability, focus = get_friend_hours(date), "friend" unless availability
end

or using an iterator:

def hours_occupied(date)
  availability = focus = nil
  %w(work family friend).each {|type| availability, focus = self.send(:"get_#{type}_hours", date), type unless availability}
end
like image 28
trushkevich Avatar answered Oct 15 '22 01:10

trushkevich


A case when is also an option:

focus = case availability
when get_work_hours(date)
  "work"
when get_family_hours(date)
  "family"
when get_friend_hours(date)
  "friends"
end
like image 32
steenslag Avatar answered Oct 15 '22 01:10

steenslag