Ruby 2.7 の変更点 - Array / Enumerable / Enumerator

Ruby 2.7 アドベントカレンダーの14日目の記事です。

qiita.com

Array

Array#intersection 追加

Array#& の複数引数版です。self と引数の配列の共通の要素を含む配列を返します。

ary = [1, 1, 2, 3, 3]
ary.intersection                  #=> [1, 1, 2, 3, 3]
ary.intersection([1, 2, 3])       #=> [1, 2, 3]
ary.intersection([1, 2], [2, 3])  #=> [2]

次のコードとだいたい同じ。

def intersection(*args)
  result = self
  args.each do |ary|
    result &= ary
  end
  result
end

Enumerable

Enumerable#filter_map 追加

filter (select) と map (collect) を組み合わせたものです。

ブロックを評価した結果のうち真(nil, false 以外の値)からなる新しい配列を返します。

[1, 2, 3, 4].filter_map { |n| n.even? ? n * 2 : nil} #=> [4, 8]

次のコードとだいたい同じ。

obj.map{...}.select(&:itself)

map{...}.compact と似てるかもしれません。ただし、compact が取り除くのは nil だけですが filter_map では false も取り除かれます。

[true, false, nil].compact              #=> [true, false]
[true, false, nil].filter_map(&:itself) #=> [true]

自分は filter ではなく select 派だったのですが、select_map というエイリアスは無いようです。

Enumerable#tally 追加

各要素の個数を Hash で返します。

'あおはあいよりいでてあいよりあおし'.chars.tally
#=> {"あ"=>4, "お"=>2, "は"=>1, "い"=>3, "よ"=>2, "り"=>2, "で"=>1, "て"=>1, "し"=>1}
'いろはにほへとちりぬるを'.chars.tally
#=> {"い"=>1, "ろ"=>1, "は"=>1, "に"=>1, "ほ"=>1, "へ"=>1, "と"=>1, "ち"=>1, "り"=>1, "ぬ"=>1, "る"=>1, "を"=>1}

Enumerator

Enumerator.produce 追加

ブロックを評価した結果が次の値となるような無限にくりかえす Enumerator を返します。 引数を与えなければ初期値は nil です。

e = Enumerator.produce(0){|i| i += 1}
e.next  #=> 0
e.next  #=> 1
e.next  #=> 2
e.next  #=> 3

Ruby のドキュメントには次のような例が載っています。

# 次の火曜日を見つける
require 'date'
Enumerator.produce(Date.today, &:succ).detect(&:tuesday?)
=> #<Date: 2019-12-17 ((2458835j,0s,0n),+0s,2299161j)>

Enumerator::Lazy#eager 追加

Enumerator::Lazy オブジェクトを Enumerator にしたものを返します。

e = [1, 2, 3].lazy  #=> #<Enumerator::Lazy: [1, 2, 3]>
e.eager             #=> #<Enumerator: #<Enumerator::Lazy: [1, 2, 3]>:each>

Enumerator::Yielder#to_proc 追加

Enumerator::Yielder#<< を呼ぶような Proc を返します。

e = Enumerator.new do |y|
  [1, 2, 3].each do |n|
    y << n
  end
end
e.entries  #=> [1, 2, 3]

これを次のように書けます。

e = Enumerator.new do |y|
  [1, 2, 3].each(&y)
end
e.entries  #=> [1, 2, 3]