(1..9) と (1...10)
Ruby で Range オブジェクトを作成する場合によく使われる記法として (開始)..(終了)
って表現があります。そして忘れがちですが (開始)...(終了)
といったドット3つの初期化方法もあります。
(1..9) # 1..9
(1...10) # 1...10
そして、上記の 1..9
と 1...10
の 2つのオブジェクトは、意味的には同じように感じますが比較すると false を返します。
(1..9).to_a == (1...10).to_a # true
(1..9) == (1...10) # false
上記は厳密に違う意味があるオブジェクトで、具体的には exclude_end?
ってフラグを持ってます。これは Range.new
の第三引数でも指定出来ます。
Range.new(1, 9) # 1..9
Range.new(1, 9, false) # 1..9
Range.new(1, 9, true) # 1...9
(1..9).exclude_end? # false
(1...10).exclude_end? # true
usecase
Range
を作るとき、end 側の指定で hoge - 1
みたいにわざわざ 1つ減算して与えるくらいならば、
ドット3つ付けて exclude_end な Range
を作るとシンプルな記述になる場合があります。
n = 4
s = "hogehoge"
puts s[0..(n-1)] # こっちより...
puts s[0...n] # こっちの方が意図が伝わりやすい(か?)
require 'date'
d = Date.new(2013, 4)
# 翌月1日を含まない範囲、すなわち当月の範囲の日付配列
(d...(d >> 1)).map(&:to_s)
# => [ "2013-04-01","2013-04-02","2013-04-03",..."2013-04-30"]
ActiveRecord で範囲を指定する際にも終端を含めない条件を生成できます。
Table.where(id: (1..10)).to_sql
# {途中省略} FROM `tables` WHERE `tables`.`id` BETWEEN 1 AND 10
Table.where(id: (1...10)).to_sql
# {途中省略} FROM `tables` WHERE (`tables`.`id` >= 1 AND `tables`.`id` < 10)