Skip over iteration in Enumerable#collect

Andrew Marshall picture Andrew Marshall · Mar 1, 2011 · Viewed 37.7k times · Source
(1..4).collect do |x|
  next if x == 3
  x + 1
end # => [2, 3, nil, 5]
    # desired => [2, 3, 5]

If the condition for next is met, collect puts nil in the array, whereas what I'm trying to do is put no element in the returned array if the condition is met. Is this possible without calling delete_if { |x| x == nil } on the returned array?

(Using Ruby 1.8.7; my code excerpt is heavily abstracted)

Answer

Mladen Jablanović picture Mladen Jablanović · Mar 1, 2011

There is method Enumerable#reject which serves just the purpose:

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

The practice of directly using an output of one method as an input of another is called method chaining and is very common in Ruby.

BTW, map (or collect) is used for direct mapping of input enumerable to the output one. If you need to output different number of elements, chances are that you need another method of Enumerable.

Edit: If you are bothered by the fact that some of the elements are iterated twice, you can use less elegant solution based on inject (or its similar method named each_with_object):

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