233

I've got the following logic in my code:

if [email protected]?(p.name)
  ...
end

@players is an array. Is there a method so I can avoid the !?

Ideally, this snippet would be:

if @players.does_not_include?(p.name)
  ...
end
2
  • 1
    is the do valid ruby? i get an error syntax error, unexpected end-of-input (works if i remove the do) Commented Jul 17, 2018 at 5:20
  • See stackoverflow.com/a/60404934/128421 for benchmarks for searching an array vs. a set. Commented Feb 26, 2020 at 8:49

14 Answers 14

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

ActiveSupport adds the exclude? method to Array, Hash, and String. This is not pure Ruby, but is used by a LOT of rubyists.

Source: Active Support Core Extensions (Rails Guides)

Sign up to request clarification or add additional context in comments.

4 Comments

Minor remark: AS adds it to Enumerable so you can use it with all classes which include Enumerable. Even with Hash. :)
This is should be green ticked
The method is no longer auto-loaded in Rails 4.2.3 (and possibly earlier), you'll need to require 'active_support/core_ext/enumerable'
110

Here you go:

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

You might have a look at the Ruby Style Guide for more info on similar techniques.

6 Comments

Upvote for the style guide. Just the reading material I needed.
readable for one statement. if many then better use negate ! or add your own method to ruby array class.
Prefer this to the exclude? answer as it's pure Ruby.
But still weird when working with a compound condition. if flag unless @players.include?(p.name) is awkward and if flag && [email protected]?(p.name) uses negation.
While if let only true pass the condition, unless lets pass false and nil. This sometimes lead to hard to find bugs. Therefore I prefer exclude?
|
19

Looking at Ruby only:

TL;DR

Use none? passing it a block with == for the comparison:

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

Array#include? accepts one argument and uses == to check against each element in the array:

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

Enumerable#none? can also accept one argument in which case it uses === for the comparison. To get the opposing behaviour to include? we omit the parameter and pass it a block using == for the comparison.

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

In the above example we can actually use:

player.none?(7)
 #=> true

That's because Integer#== and Integer#=== are equivalent. But consider:

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

none? returns false because Integer === 1 #=> true. But really a legit notinclude? method should return true. So as we did before:

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

Comments

13

How about the following:

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

2 Comments

Man, with the speed that answers come in on this site, i don't know if i'll get any reputation without some good-ole nepotism! :-D The Style Guide link is a nice touch.
It's not about being fast. It's about being thorough. (I've found) =)
8
module Enumerable
  def does_not_include?(item)
    !include?(item)
  end
end

Ok, but seriously, the unless works fine.

1 Comment

+1 unless it's ok for the snippet showed, but the condition may be more complex. I think it's handy to have these negated methods, they allow more declarative code.
8

If your objection to the !-operator is primarily that it needs to be put in front of your check and this breaks your typing flow, then there is the .! method. You just put it after the check to invert the boolean:

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

Comments

7

Use unless:

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

Comments

3

Try this, it's pure Ruby so there's no need to add any peripheral frameworks

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

I was struggling with a similar logic for a few days, and after checking several forums and Q&A boards to little avail it turns out the solution was actually pretty simple.

Comments

2

Can you use:

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

unless is opposite of if, or you may use reject.

You can reject the not-required elements:

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

after the getting the results you can do your implementation.

Comments

1

Using unless is fine for statements with single include? clauses but, for example, when you need to check the inclusion of something in one Array but not in another, the use of include? with exclude? is much friendlier.

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

But as dizzy42 says above, the use of exclude? requires ActiveSupport

1 Comment

It just needs the use of Active Support's Core Extensions. guides.rubyonrails.org/v3.2/…
1

I was looking up on this for myself, found this, and then a solution. People are using confusing methods and some methods that don't work in certain situations or not at all.

I know it's too late now, considering this was posted 6 years ago, but hopefully future visitors find this (and hopefully, it can clean up their, and your, code.)

Simple solution:

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

1 Comment

I don't like it in this context but I'm genuinely surprised that you can use not to substitute !. Been using Ruby for 15 years and that's new to me. Thanks for posting!
0

Try something like this:

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

Comments

0

It's not a single method, but chaining count and zero works:

[1, 2, 3].count(1).zero? # => false
[1, 2, 3].count(4).zero? # => true

It also works with strings:

"hello".count("l").zero? # => false
"hello".count("q").zero? # => true

Comments

0

Rails contains value.in?(array)which also do the same thing as array.include?(value)
The matter is readability. Use whatever best according to your variables
Below is an example

a = 3
b = [1,2,3,5]
a.in?(b) # => true
a = 4
a.in?(b) # => false

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.