Czy istnieje przeciwieństwo włączenia? dla Ruby Arrays?


190

W moim kodzie mam następującą logikę:

if !@players.include?(p.name)
  ...
end

@playersjest tablicą. Czy istnieje metoda, dzięki której mogę uniknąć !?

Najlepiej byłoby, gdyby ten fragment był:

if @players.does_not_include?(p.name)
  ...
end

1
czy jest doprawidłowy rubin? dostaję błąd syntax error, unexpected end-of-input(działa, jeśli do
usunę

Zobacz stackoverflow.com/a/60404934/128421, aby zapoznać się z testami porównawczymi do przeszukiwania tablicy vs. zestawu.
Tin Man

Odpowiedzi:


370
if @players.exclude?(p.name)
    ...
end

ActiveSupport dodaje exclude?metodę Array, Hashoraz String. To nie jest czysty rubin, ale jest używany przez WIELU rubinów.

Źródło: Główne rozszerzenia Active Support (przewodniki po szynach)



3
Drobna uwaga: AS dodaje go do Enumerable, abyś mógł używać go ze wszystkimi klasami, które zawierają Enumerable. Nawet z hashem. :)
user2422869,

1
To powinno być zaznaczone na zielono
etlds

Ta metoda nie jest już automatycznie ładowana w Railsach 4.2.3 (i być może wcześniej), musiszrequire 'active_support/core_ext/enumerable'
Dennis

96

Proszę bardzo:

unless @players.include?(p.name)
  ...
end

Możesz zajrzeć do Przewodnika po stylu Ruby, aby uzyskać więcej informacji na temat podobnych technik.


Głosowanie za przewodnikiem po stylu. Potrzebny mi tylko materiał do czytania.
justnorris,

1
czytelny dla jednej instrukcji. jeśli wielu, lepiej użyj negacji! lub dodaj własną metodę do klasy tablicy ruby.
Renars Sirotins

3
Wolisz to niż wykluczenie? odpowiedz, bo to czysty rubin.
dscher

1
Ale nadal dziwne, gdy pracujesz ze złożonym stanem. if flag unless @players.include?(p.name)jest niezręczny i if flag && !@players.include?(p.name)używa negacji.
gabe

1
Chociaż iftylko niech trueprzechodzą stan, unlesspozwala przejść falsei nil. To czasami prowadzi do trudnych do znalezienia błędów. Dlatego wolęexclude?
ToTenMilan

12

Co powiesz na następujące:

unless @players.include?(p.name)
  ....
end

Człowieku, z szybkością, z jaką pojawiają się odpowiedzi na tej stronie, nie wiem, czy zdobędę reputację bez dobrego nepotyzmu! :-D Link do przewodnika po stylach to miły akcent.
ilasno

2
Tu nie chodzi o bycie szybkim. Chodzi o bycie dokładnym. (Znalazłem) =)
Charles Caldwell

11

Patrząc tylko na Ruby:

TL; DR

Użyj do porównania none?bloku z ==do:

[1, 2].include?(1)
  #=> true
[1, 2].none? { |n| 1 == n  }
  #=> false

Array#include?akceptuje jeden argument i używa ==do sprawdzenia każdego elementu w tablicy:

player = [1, 2, 3]
player.include?(1)
 #=> true

Enumerable#none?może również zaakceptować jeden argument, w którym to przypadku używa go ===do porównania. Aby uzyskać przeciwne zachowanie include?, pomijamy parametr i przekazujemy go do bloku za pomocą ==porównania.

player.none? { |n| 7 == n }
 #=> true 
!player.include?(7)    #notice the '!'
 #=> true

W powyższym przykładzie możemy faktycznie użyć:

player.none?(7)
 #=> true

To dlatego, że Integer#==i Integer#===są równoważne. Ale zastanów się:

player.include?(Integer)
 #=> false
player.none?(Integer)
 #=> false

none?zwraca falseponieważ Integer === 1 #=> true. Ale naprawdę legalna notinclude?metoda powinna powrócić true. Tak jak wcześniej:

player.none? { |e| Integer == e  }
 #=> true

7
module Enumerable
  def does_not_include?(item)
    !include?(item)
  end
end

Ok, ale poważnie, chyba że działa dobrze.


1
+1 unlessjest w porządku dla pokazanego fragmentu, ale warunek może być bardziej złożony. Myślę, że warto mieć te zanegowane metody, które pozwalają na bardziej deklaratywny kod.
tokland


1

Czy możesz użyć:

unless @players.include?(p.name) do
...
end

unlessjest przeciwieństwem iflub możesz użyć reject.

Możesz rejectniepotrzebne elementy:

@players.reject{|x| x==p.name}

po uzyskaniu wyników możesz wykonać swoją implementację.


0

Używanie unlessjest w porządku dla instrukcji z pojedynczymi include?klauzulami, ale na przykład, gdy trzeba sprawdzić włączenie czegoś w jednym, Arrayale nie w drugim, użycie include?z exclude?jest znacznie bardziej przyjazne.

if @players.include? && @spectators.exclude? do
  ....
end

Ale jak mówi dizzy42 powyżej, użycie exclude?wymaga ActiveSupport


Po prostu potrzebuje użycia podstawowych rozszerzeń Active Support. guide.rubyonrails.org/v3.2/…
Tin Man

0

Wypróbuj coś takiego:

@players.include?(p.name) ? false : true

0

Spróbuj, to jest czysty Ruby, więc nie trzeba dodawać żadnych peryferyjnych ram

if @players.include?(p.name) == false do 
  ...
end

Przez kilka dni zmagałem się z podobną logiką, a po sprawdzeniu kilku forów i forum pytań i odpowiedzi okazało się, że rozwiązanie było dość proste.


0

Patrzyłem na to dla siebie, znalazłem to, a potem rozwiązanie. Ludzie używają mylących metod i niektórych metod, które nie działają w określonych sytuacjach lub wcale.

Wiem, że jest już za późno, biorąc pod uwagę, że został opublikowany 6 lat temu, ale mam nadzieję, że przyszli odwiedzający to znajdą (i mam nadzieję, że to może wyczyścić ich i twój kod).

Proste rozwiązanie:

if not @players.include?(p.name) do
  ....
end
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.