Skip over iteration in Enumerable#collect

不问归期 提交于 2019-11-28 16:12:34

问题


(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)


回答1:


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}



回答2:


I would simply call .compact on the resultant array, which removes any instances of nil in an array. If you'd like it to modify the existing array (no reason not to), use .compact!:

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



回答3:


just a suggestion, why don't you do it this way:

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

in that way you saved another iteration to remove nil elements from the array. hope it helps =)




回答4:


i would suggest to use:

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

instead of the collect + next statement.




回答5:


You could pull the decision-making into a helper method, and use it via 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]



回答6:


Ruby 2.7+

There is now!

Ruby 2.7 is introducing filter_map for this exact purpose. It's idiomatic and performant, and I'd expect it to become the norm very soon.

For example:

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

Here's a good read on the subject.

Hope that's useful to someone!



来源:https://stackoverflow.com/questions/5152098/skip-over-iteration-in-enumerablecollect

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!