314

How is the conditional operator (? :) used in Ruby?

For example, is this correct?

<% question = question.size > 20 ? question.question.slice(0, 20)+"..." : question.question %>
Wayne Conrad
  • 90,071
  • 22
  • 147
  • 183
Mithun Sreedharan
  • 45,549
  • 69
  • 171
  • 232
  • 1
    yes, I think, but I also think you could accomplish that by: `question=question[0,20]` If it was smaller than 20, it won't change it any. – DGM Nov 23 '10 at 05:06
  • i also need to add a '...' if length is greater than 20 – Mithun Sreedharan Nov 23 '10 at 05:32
  • 1
    Be careful blindly chopping off a line at a given column. You can end up cutting a word midway then appending the elipsis ('...'), which looks bad. Instead, look for a nearby punctuation or whitespace character, and truncate there. Only if there is no better breaking point nearby should you truncate mid-word. – the Tin Man Nov 23 '10 at 06:11

7 Answers7

507

It is the ternary operator, and it works like in C (the parenthesis are not required). It's an expression that works like:

if_this_is_a_true_value ? then_the_result_is_this : else_it_is_this

However, in Ruby, if is also an expression so: if a then b else c end === a ? b : c, except for precedence issues. Both are expressions.

Examples:

puts (if 1 then 2 else 3 end) # => 2

puts 1 ? 2 : 3                # => 2

x = if 1 then 2 else 3 end
puts x                        # => 2

Note that in the first case parenthesis are required (otherwise Ruby is confused because it thinks it is puts if 1 with some extra junk after it), but they are not required in the last case as said issue does not arise.

You can use the "long-if" form for readability on multiple lines:

question = if question.size > 20 then
  question.slice(0, 20) + "..."
else 
  question
end
the Tin Man
  • 150,910
  • 39
  • 198
  • 279
36
puts true ? "true" : "false"
=> "true"


puts false ? "true" : "false"
=> "false"
DGM
  • 25,680
  • 6
  • 56
  • 79
  • Terse but explains what it does. – the Tin Man Oct 02 '14 at 23:17
  • 4
    Small edit `puts (true ? "true" : "false")` with parenthesis. Otherwise the order of operations is not clear. When I first read this I was confused as I read it as `(puts true) ? "true" : "false"` then expected `puts` to return the boolean which then became the string value. – Fresheyeball Aug 25 '15 at 23:16
26

Your use of ERB suggests that you are in Rails. If so, then consider truncate, a built-in helper which will do the job for you:

<% question = truncate(question, :length=>30) %>
the Tin Man
  • 150,910
  • 39
  • 198
  • 279
Wayne Conrad
  • 90,071
  • 22
  • 147
  • 183
18

@pst gave a great answer, but I'd like to mention that in Ruby the ternary operator is written on one line to be syntactically correct, unlike Perl and C where we can write it on multiple lines:

(true) ? 1 : 0

Normally Ruby will raise an error if you attempt to split it across multiple lines, but you can use the \ line-continuation symbol at the end of a line and Ruby will be happy:

(true)   \
  ? 1    \
  : 0

This is a simple example, but it can be very useful when dealing with longer lines as it keeps the code nicely laid out.

It's also possible to use the ternary without the line-continuation characters by putting the operators last on the line, but I don't like or recommend it:

(true) ?
  1 :
  0

I think that leads to really hard to read code as the conditional test and/or results get longer.

I've read comments saying not to use the ternary operator because it's confusing, but that is a bad reason to not use something. By the same logic we shouldn't use regular expressions, range operators ('..' and the seemingly unknown "flip-flop" variation). They're powerful when used correctly, so we should learn to use them correctly.


Why have you put brackets around true?

Consider the OP's example:

<% question = question.size > 20 ? question.question.slice(0, 20)+"..." : question.question %>

Wrapping the conditional test helps make it more readable because it visually separates the test:

<% question = (question.size > 20) ? question.question.slice(0, 20)+"..." : question.question %>

Of course, the whole example could be made a lot more readable by using some judicious additions of whitespace. This is untested but you'll get the idea:

<% question = (question.size > 20) ? question.question.slice(0, 20) + "..." \
                                   : question.question 
%>

Or, more written more idiomatically:

<% question = if (question.size > 20)
                question.question.slice(0, 20) + "..."
              else 
                question.question 
              end
%>

It'd be easy to argument that readability suffers badly from question.question too.

the Tin Man
  • 150,910
  • 39
  • 198
  • 279
  • 2
    If multi-line, why not just use if...else...end? – Wayne Conrad Nov 23 '10 at 16:44
  • 2
    Because of too many years working in Perl and C? I use either, depending on the situation and whether one is visually clearer than the other. Sometimes if/else is too verbose, sometimes ?: is ugly. – the Tin Man Nov 23 '10 at 16:47
  • 1
    @WayneConrad The if has at least one problem explained in this answer: http://stackoverflow.com/a/4252945/2597260 Compare a few ways of using multiline if/ternary operator: https://gist.github.com/nedzadarek/0f9f99755d42bad10c30 – Darek Nędza Dec 17 '14 at 18:14
  • Why have you put brackets around `true`? – Zac Oct 07 '16 at 17:05
  • 2
    Because `true` is actually sitting in for what would be an expression that evaluates to `true` or `false`. It's better to visually delimit those since ternary statements can quickly devolve into visual noise, reducing readability which affects maintainability. – the Tin Man Oct 07 '16 at 17:17
3

A simple example where the operator checks if player's id is 1 and sets enemy id depending on the result

player_id=1
....
player_id==1? enemy_id=2 : enemy_id=1
# => enemy=2

And I found a post about to the topic which seems pretty helpful.

devwanderer
  • 75
  • 1
  • 9
0

The code condition ? statement_A : statement_B is equivalent to

if condition == true
  statement_A
else
  statement_B
end
Umesh Malhotra
  • 665
  • 7
  • 22
0

Easiest way:

param_a = 1
param_b = 2

result = param_a === param_b ? 'Same!' : 'Not same!'

since param_a is not equal to param_b then the result's value will be Not same!

Adrian Eranzi
  • 35
  • 1
  • 7