Есть ли в Ruby метод, который делает противоположность find? - PullRequest
7 голосов
/ 29 апреля 2011
a, b, c = 0, 1, 2
[a, b, c].find(&:zero?) # => 0

Есть ли метод, который находит первый элемент, для которого блок возвращает false?

[a, b, c].the_method(&:zero?) # => 1

Другими словами, он будет вести себя так же, как:

[a, b, c].reject(&:zero?).first

Ответы [ 3 ]

10 голосов
/ 29 апреля 2011

Там - это не , но вы можете создать один из них, используя способ очистки:

a = [0,2,1,0,3]

module Enumerable
  def first_not(&block)
    find{ |x| !block[x] }
  end
end

p a.first_not(&:zero?)
#=> 2

... или ужасно забавный способ взлома:

class Proc
  def !
    proc{ |o,*a| !self[o,*a] }
  end
end

p a.find(&!(:zero?.to_proc))
#=> 2

... или кратко, но ужасно опасный способ:

class Symbol
  def !
    proc{ |o,*a| !o.send(self,*a) }
  end
end

p a.find(&!:zero?)
#=> 2

Но я бы выступил за то, чтобы просто пропустить хитрое использование Symbol#to_proc и сказать, что вы хотите:

p a.find{ |i| !i.zero? }
#=> 2
2 голосов
/ 22 марта 2012

Если вы используете Ruby 2.0, вы можете сделать lazy.reject(&:zero?).first без потери производительности при просмотре полного массива.

2 голосов
/ 29 апреля 2011

Насколько я могу судить, для этого нет стандартного метода (учитывая, что find_all и reject ссылаются друг на друга, но find ничего не ссылается). Если вам это нужно часто (особенно если отклонение слишком медленное), вы можете написать свой

module Enumerable

  def first_reject(&block)
    find {|a| not (block.call(a)) }
  end

end
...