Can I use `else if` over `elsif`?

  1. Is it safe to use else if over elsif?
  2. Is it better to use elsif because it follows Ruby's typing convention?
  3. Or is this a preference?

This is a piece of code taken from a book. I added extra end keywords and swapped elsif keywords with else ifs.

def describe(inhabitant)
  if inhabitant == "sophie"
    puts 'gender: female'
    puts 'height: 145'
  else if inhabitant == "paul"
    puts 'gender: male'
    puts 'height: 145'
  else if inhabitant == "dawn"
    puts 'gender: female'
    puts 'height: 170'
  else if inhabitant == "brian"
    puts 'gender: male'
    puts 'height: 180'
  else if 
    puts 'species: Trachemys scripta elegans'
    puts 'height: 6'
  end
  end 
  end   
  end
  end
end

This made me realize how ugly else if is.


You can use else if and it's safe. However note that this means extra end keywords are needed.

if n == 1
  puts "foo"
elsif n == 2
  puts "bar"
end

is logically the same as:

if n == 1
  puts "foo"
else if n == 2
       puts "bar"
     end
end

or the equivalent:

if n == 1
  puts "foo"
else 
  if n == 2
    puts "bar"
  end
end

TL;DR - Replacing elsif with else if is alright for a conditional with only 2 paths. Remember to close the second if conditional created with else if. It is best practice to have as few levels of conditionals as you can, making for a less-complex method. So err on the side of caution and use elsif.

Depending on how you plan to write your method, else if may work. It is not a good habit to get into, however.

Take the following example. There are only 2 conditions. The second condition looks similar to elsif, but is interpreted as the second chunk of code:

# What you may want to write
if true
  puts 'true'
else if false
  puts 'false'
end

# How Ruby requires it
if true
  puts 'true'
else
  if false  # You may also do: puts 'false' if false
    puts 'false'
  end
end

The first block will search for another end to close the primary conditional. Take note, you can bypass the extra end with a single line if statement. (I only suggest this if the executed code can be written on a single line.)

It is important to note that once you declare an else, you may have no other conditionals in the same tier as that else. Given the second example above, the second if is nested under the else. If you were to call else or elsif on the same tier as the initial else, the conditional will fail.

Here is when you would not want to implement else if:

def describe(inhabitant)
  if inhabitant == "sophie"
    puts 'gender: female'
    puts 'height: 145'
  elsif inhabitant == "paul"
    puts 'gender: male'
    puts 'height: 145'
  elsif inhabitant == "dawn"
    puts 'gender: female'
    puts 'height: 170'
  elsif inhabitant == "brian"
    puts 'gender: male'
    puts 'height: 180'
  else
    puts 'species: Trachemys scripta elegans'
    puts 'height: 6'
  end
end

Notice that neither of the elsif statements can be "converted" to else if in a clean way.

UPDATE: Thanks to Stefan, you can still use else if, leading to a very nested method.

https://gist.github.com/sos4nt/a41b36d21f6eec5e0a42


I would prefer elsif over else if. But that is just my opinion and technically there is no difference.

But I would suggest to use a case block instead of multiple elsif your example:

def describe(inhabitant)
  case inhabitant
  when "sophie"
    puts 'gender: female'
    puts 'height: 145'
  when "paul"
    puts 'gender: male'
    puts 'height: 145'
  when "dawn"
    puts 'gender: female'
    puts 'height: 170'
  when "brian"
    puts 'gender: male'
    puts 'height: 180'
  else
    puts 'species: Trachemys scripta elegans'
    puts 'height: 6'
  end

Or I would store that mapping in a hash:

PEOPLE = {
  'sophie' => { :gender => :female, :height => 145 },
  'paul'   => { :gender => :male,   :height => 145 },
  # ...
}

def describe(inhabitant)
  description = PEOPLE.fetch(
    inhabitant, { :species => 'Trachemys scripta elegans', :height => 6 }
  )

  puts "gender: #{description[:gender]}"    if description[:gender]
  puts "species: #{description[:species]}"  if description[:species]
  puts "height: #{description[:height]}"
end