Ruby if else in array.each

13,870

Solution 1

The syntax of a conditional expression in Ruby is:

if c_1 then e_1 elsif c_2 then e_2 elsif c_3 then e_3 … elsif c_n then e_n else e_nplus1 end

where c_1c_n and e_1e_nplus1 can be arbitrary Ruby expressions.

It is possible to use an expression separator (i.e. ; or newline) instead of the then keyword to separate the parts of the conditional expression.

With semicolon (this usage is non-idiomatic):

if c_1; e_1 elsif c_2; e_2 elsif c_3; e_3 … elsif c_n; e_n else e_nplus1 end

With newlines:

if c_1
  e_1
elsif c_2
  e_2
elsif c_3
  e_3
# …
elsif c_n
  e_n
else
  e_nplus1
end

If you use newlines, you can optionally also use the then keyword, but that is non-idiomatic, too:

if c_1
then e_1
elsif c_2
then e_2
elsif c_3
then e_3
# …
elsif c_n
then e_n
else
  e_nplus1
end

So, in your case, the correct syntax would be:

# idiomatic
a.each { |i| if i % 3 == 0 then puts "three" elsif i % 5 == 0 then puts "five" else puts i end }

# non-idiomatic
a.each { |i| if i % 3 == 0; puts "three" elsif i % 5 == 0; puts "five" else puts i end }

# idiomatic
a.each { |i|
  if i % 3 == 0
    puts "three"
  elsif i % 5 == 0
    puts "five"
  else
    puts i
  end
}

# non-idiomatic
a.each { |i|
  if i % 3 == 0
  then puts "three"
  elsif i % 5 == 0
  then puts "five"
  else
    puts i
  end
}

However, for such a chain of if / elsif, it is typically more idiomatic to use a case expression:

# idiomatic
case when c_1 then e_1 when c_2 then e_2 when c_3 then e_3 … when c_n then e_n else e_nplus1 end

# non-idiomatic
case when c_1; e_1 when c_2; e_2 when c_3; e_3 … when c_n; e_n else e_nplus1 end

# idiomatic
case
when c_1
  e_1
when c_2
  e_2
when c_3
  e_3
# …
when c_n
  e_n
else
  e_nplus1
end

# non-idiomatic
case
when c_1
then e_1
when c_2
then e_2
when c_3
then e_3
# …
when c_n
then e_n
else
  e_nplus1
end

Which in your case would look like this:

# idiomatic
a.each { |i| case when i % 3 == 0 then puts "three" when i % 5 == 0 then puts "five" else puts i end }

# non-idiomatic
a.each { |i| case when i % 3 == 0; puts "three" when i % 5 == 0; puts "five" else puts i end }

# idiomatic
a.each { |i|
  case
  when i % 3 == 0
    puts "three"
  when i % 5 == 0
    puts "five"
  else
    puts i
  end
}

# non-idiomatic
a.each { |i|
  case
  when i % 3 == 0
  then puts "three"
  when i % 5 == 0
  then puts "five"
  else
    puts i
  end
}

Note that the conditional expressions (both if and case) are expressions, not statements. There are no statements in Ruby, everything is an expression, everything evaluates to a value. A conditional expression evaluates to the value of the expression in the branch that was taken.

So, you could also write it like this:

# idiomatic
a.each { |i| puts(if i % 3 == 0 then "three" elsif i % 5 == 0 then "five" else i end) }

# non-idiomatic
a.each { |i| puts(if i % 3 == 0; "three" elsif i % 5 == 0; "five" else i end) }

# idiomatic
a.each { |i|
  puts(if i % 3 == 0
    "three"
  elsif i % 5 == 0
    "five"
  else
    i
  end)
}

# non-idiomatic
a.each { |i|
  puts(if i % 3 == 0
  then "three"
  elsif i % 5 == 0
  then "five"
  else
    i
  end)
}

# idiomatic
a.each { |i| puts(case when i % 3 == 0 then "three" when i % 5 == 0 then "five" else i end) }

# non-idiomatic
a.each { |i| puts(case when i % 3 == 0; "three" when i % 5 == 0; "five" else i end) }

# idiomatic
a.each { |i|
  puts(case
  when i % 3 == 0
    "three"
  when i % 5 == 0
    "five"
  else
    i
  end)
}

# non-idiomatic
a.each { |i|
  puts(case
  when i % 3 == 0
  then "three"
  when i % 5 == 0
  then "five"
  else
    i
  end)
}

Solution 2

Here is the syntax.

a = *(1..100)
a.each do |i|
  if i % 3 == 0
    puts "three"
  elsif i % 5 == 0
    puts "five"
  else
    puts i
  end
end

Keep in mind that each will return Enumerator, but not exact value. You need to use return keyword to return value. Here is the docs

Solution 3

You can use an if to qualify a single statement, but once you move into elsif/else territory, it's necessary to break it up like a normal C-style if statement:

a.each do |i|
  if i % 3 == 0 
    puts "three" 
  elsif i % 5 == 0
    puts "five" 
  else
    puts i
  end
end

Solution 4

Please see if the below code helps

def abc
   a = *(1..100)
   a.each do |i|
       if i % 3 == 0 
       puts "three"
       elsif i % 5 == 0
       puts "five"
       else
         puts i
       end
     end
   end

=> :abc

2.3.0 :013 > abc

this gives the desired output in irb mode.

Solution 5

Or, if you want to emphasize, that there is always a puts and just the output value changes:

puts(
  if i%3==0
    'three'
  elsif i%5==0
    'five'
  else
    i
  end
) 
Share:
13,870
Mike
Author by

Mike

Updated on June 04, 2022

Comments

  • Mike
    Mike almost 2 years

    New to Ruby here. I am attempting to put an if else statement in an array to send a string if a certain modulus == 0. for the life of me i can't find it anywhere. I am sure someone will find it ridiculously simple.

    a = *(1..100)
    a.each { |i| puts "three" if i % 3 == 0 elsif puts "five" if i % 5 == 0 else puts i}
    

    Just not sure of the correct syntax. Still new to ruby and am trying to learn the syntax. Took a C class last semester and my brain keeps wanting to put in C syntax.

    When I leave it as

    a = *(1..100)
    a.each { |i| puts "three" if i % 3 == 0}
    

    it works fine, just trying to figure out how to add if else to it. Help is appreciated.

    The answers below were really helpful. I am trying to take it a step further and call it into a function. It keeps returning 5, and not "five", or 3, and not "three".

    here is my function:

    def array_mod
    a = *(1..100)
    a.each { |i| if i % 3 == 0  && i % 5 == 0; i = "fifteen" elsif i % 3 == 0; i = "three" elsif i % 5 == 0; i = "five" else i = i end }
    

    end

    and here is my attempt at calling it.

    require "minitest/autorun"
    require_relative "array_modulus.rb"
    
    
    
    class TestArrayFunction < Minitest::Test
    
    
    def test_array1
    
        results = array_mod
    
        assert_equal(100, results.length)
    end
    
    def test_array2
    
        results = array_mod
        assert_equal("three", results[2])
    end
    
    
    end
    

    I was told it is not updating my array. Thanks again.

  • fl00r
    fl00r over 7 years
    Keep in mind that each will return Enumerator, but not exact value what do you mean?
  • Mike
    Mike over 7 years
    ok. I didn't know if I had to put an else inside the brackets. I am used to C's using parentheses and curled brackets for everything. Ruby hardly uses them from what ive seen so far. But the |i| , is that labeling the following code as "i"? Or is it a temporary labelling i for that block? Thanks though, this is exactly what I was curious about.
  • mwp
    mwp over 7 years
    @Mike I'm not sure you mean by "inside the brackets." The else is lined up with the if, just like it would be in C (minus the curly braces). Yeah, curlies aren't used the same way... they're used to define blocks in Ruby (similar to do..end). The |i| is just declaring the loop variable(s); each time through the loop, i will be set to the next element of a.
  • Mike
    Mike over 7 years
    Your reply was extremely helpful. I tried to ask a second question in this box but i guess they don't want you posting code in these boxes, so I updated my top question. Thanks again man.
  • Jörg W Mittag
    Jörg W Mittag over 7 years
    That's because when you have a question, you should write a question, not a comment ;-)
  • retgoat
    retgoat over 7 years
    I meant that each will return unchanged array in that case.