web-dev-qa-db-ja.com

Enumerable#collectの反復をスキップ

(1..4).collect do |x|
  next if x == 3
  x + 1
end # => [2, 3, nil, 5]
    # desired => [2, 3, 5]

nextの条件が満たされている場合、collectは配列にnilを入れますが、私がやろうとしているのはno element条件が満たされた場合に返される配列。これは、返された配列でdelete_if { |x| x == nil }を呼び出さなくても可能ですか?

(Ruby 1.8.7を使用;私のコードの抜粋はかなり抽象化されています)

54
Andrew Marshall

メソッドがあります Enumerable#reject これはちょうど目的に役立ちます:

(1..4).reject{|x| x == 3}.collect{|x| x + 1}

あるメソッドの出力を別のメソッドの入力として直接使用する方法はmethod chainingと呼ばれ、Rubyでは非常に一般的です。

ところで、map(またはcollect)は、列挙可能な入力から出力への直接マッピングに使用されます。異なる数の要素を出力する必要がある場合、Enumerableの別のメソッドが必要になる可能性があります。

編集:要素の一部が2回繰り返されるという事実に悩まされている場合、inject(または同様のメソッド each_with_object ):

(1..4).each_with_object([]){|x,a| a << x + 1 unless x == 3}
78

結果の配列で.compactを呼び出すだけで、配列内のnilのインスタンスがすべて削除されます。既存の配列を変更したい場合(変更しない理由はありません)、.compact!を使用します。

(1..4).collect do |x|
  next if x == 3
  x
end.compact!
46
Benson

ただの提案、なぜあなたはそれをこのようにしないのですか:

result = []
(1..4).each do |x|
  next if x == 3
  result << x
end
result # => [1, 2, 4]

そのようにして、別の反復を保存して、配列からnil要素を削除しました。それが役立つことを願っています=)

4
Staelen

意思決定をヘルパーメソッドに取り込み、 Enumerable#reduce

def potentially_keep(list, i)
  if i === 3
    list
  else
    list.Push i
  end
end
# => :potentially_keep

(1..4).reduce([]) { |memo, i| potentially_keep(memo, i) }
# => [1, 2, 4]
0
alxndr

私は使用することをお勧めします:

(1..4).to_a.delete_if {|x| x == 3}

collect + nextステートメントの代わりに。

0
ALoR

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]

ここに 主題に関する良い読み物 があります。

それが誰かに役立つことを願っています!

0
SRack