juil
juil

Reputation: 2488

Difference between '..' (double-dot) and '...' (triple-dot) in range generation?

I've just started learning Ruby and Ruby on Rails and came across validation code that uses ranges:

validates_inclusion_of :age, :in => 21..99
validates_exclusion_of :age, :in => 0...21, :message => "Sorry, you must be over 21"

At first I thought the difference was in the inclusion of endpoints, but in the API docs I looked into, it didn't seem to matter whether it was .. or ...: it always included the endpoints.

However, I did some testing in irb and it seemed to indicate that .. includes both endpoints, while ... only included the lower bound but not the upper one. Is this correct?

Upvotes: 156

Views: 80431

Answers (5)

Andrew Marshall
Andrew Marshall

Reputation: 96934

The documentation for Range says this:

Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.

So a..b is like [a, b] a.k.a. a <= x <= b, whereas a...b is like [a, b) a.k.a. a <= x < b.


Note that, while to_a on a Range of integers gives a collection of integers, a Range is not a set of values, but simply a pair of start/end values:

(1..5).include?(5)           #=> true
(1...5).include?(5)          #=> false

(1..4).include?(4.1)         #=> false
(1...5).include?(4.1)        #=> true
(1..4).to_a == (1...5).to_a  #=> true
(1..4) == (1...5)            #=> false

The docs used to not include this, instead requiring reading the [Pickaxe’s section on Ranges][2]. Thanks to @MarkAmery ([see below][3]) for noting this update.

Upvotes: 215

Dennis
Dennis

Reputation: 59509

a...b excludes the end value, while a..b includes the end value.

When working with integers, a...b behaves as a..b-1.

>> (-1...3).to_a
=> [-1, 0, 1, 2]

>> (-1..2).to_a
=> [-1, 0, 1, 2]

>> (-1..2).to_a == (-1...3).to_a
=> true

But really the ranges differ on a real number line.

>> (-1..2) == (-1...3)
=> false

You can see this when incrementing in fractional steps.

>> (-1..2).step(0.5).to_a
=> [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]

>> (-1...3).step(0.5).to_a
=> [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5]

Upvotes: 2

Mark Amery
Mark Amery

Reputation: 154695

The API docs now describe this behaviour:

Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.

-- http://ruby-doc.org/core-2.1.3/Range.html

In other words:

2.1.3 :001 > ('a'...'d').to_a
 => ["a", "b", "c"] 
2.1.3 :002 > ('a'..'d').to_a
 => ["a", "b", "c", "d"] 

Upvotes: 7

daniel
daniel

Reputation: 9835

.. and ... denote a range.

Just see it in irb:

ruby-1.9.2-p290 :032 > (1...2).each do puts "p" end
p
 => 1...2 
ruby-1.9.2-p290 :033 > (1..2).each do puts "p" end
p
p

Upvotes: -5

Chris Heald
Chris Heald

Reputation: 62648

That is correct.

1.9.3p0 :005 > (1...10).to_a
 => [1, 2, 3, 4, 5, 6, 7, 8, 9]
1.9.3p0 :006 > (1..10).to_a
 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

The triple-dot syntax is less common, but is nicer than (1..10-1).to_a

Upvotes: 9

Related Questions