1. ホーム
  2. ruby

[解決済み] Rubyでnil値をマッピングして削除する方法

2022-02-11 14:43:40

質問

を持っています。 map これは、値を変更するか、またはnilに設定します。そして、リストからnilのエントリーを削除したい。リストは保持する必要はありません。

現在、私が持っているのはこれです。

# A simple example function, which returns a value or nil
def transform(n)
  rand > 0.5 ? n * 10 : nil }
end

items.map! { |x| transform(x) } # [1, 2, 3, 4, 5] => [10, nil, 30, 40, nil]
items.reject! { |x| x.nil? } # [10, nil, 30, 40, nil] => [10, 30, 40]

このようにループして条件付きで別の配列に集めればいいのは承知しています。

new_items = []
items.each do |x|
    x = transform(x)
    new_items.append(x) unless x.nil?
end
items = new_items

でも、そんなイディオム的なものではなさそうです。リスト上の関数をマッピングして、nilを削除したり除外したりする良い方法はないでしょうか?

どのように解決するのですか?

Ruby 2.7+

今ならあります!

Ruby 2.7で導入されるのは filter_map この目的のために 慣用的でパフォーマンスも高いので、すぐに標準になると思います。

例えば、こんな感じです。

numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]

あなたの場合、ブロックがfalseyと評価されるように、単に。

items.filter_map { |x| process_x url }

" Ruby 2.7 では Enumerable#filter_map が追加されました。 この問題に対する初期のアプローチに対するパフォーマンス・ベンチマークを含む、この問題に関する良い読み物です。

N = 100_000
enum = 1.upto(1_000)
Benchmark.bmbm do |x|
  x.report("select + map")  { N.times { enum.select { |i| i.even? }.map{ |i| i + 1 } } }
  x.report("map + compact") { N.times { enum.map { |i| i + 1 if i.even? }.compact } }
  x.report("filter_map")    { N.times { enum.filter_map { |i| i + 1 if i.even? } } }
end

# Rehearsal -------------------------------------------------
# select + map    8.569651   0.051319   8.620970 (  8.632449)
# map + compact   7.392666   0.133964   7.526630 (  7.538013)
# filter_map      6.923772   0.022314   6.946086 (  6.956135)
# --------------------------------------- total: 23.093686sec
# 
#                     user     system      total        real
# select + map    8.550637   0.033190   8.583827 (  8.597627)
# map + compact   7.263667   0.131180   7.394847 (  7.405570)
# filter_map      6.761388   0.018223   6.779611 (  6.790559)