rubyguides

Array#include?

arr.include?(object) → true or false

Array#include? returns true when any element of the array is == to the argument, and false otherwise. It is the Ruby equivalent of “does this list contain X” — a direct membership test that short-circuits on the first match. It is the search method most Ruby code reaches for when checking whether a value is in an array.

[1, 2, 3].include?(2)       # => true
[1, 2, 3].include?(4)       # => false
["a", "b"].include?("c")    # => false
[].include?("anything")     # => false

The scan walks elements left-to-right and stops at the first match, but the return value is always a plain boolean. If you need the matching element itself, use Array#find instead. If you need a block form (for predicates, regex matches, or attribute checks), use Array#any? instead.

Also called member?

Array#member? is an alias of include? inherited from Enumerable. They are the same method under two names, so method(:include?) == method(:member?) returns true on any array. member? exists because include? is part of the enumerable contract for any collection that mixes in the module; Array simply keeps the same name for both. Pick the spelling you prefer and stick with it; include? is the more common form in the Ruby community.

[:draft, :published].include?(:draft)    # => true
[:draft, :published].member?(:published)  # => true

Equality semantics

include? uses Object#== for comparison. That gives the behavior most readers expect for built-in types — 1 == 1, "x" == "x", :a == :a — and applies recursively to nested arrays and hashes. These are the same equality semantics used by Array#uniq, Array#-, and the rest of the array methods, so results stay consistent across the API.

[[1, 2], [3, 4]].include?([1, 2])             # => true
[{ a: 1 }, { a: 2 }].include?({ a: 1 })       # => true

A few consequences worth knowing:

  • No type coercion. [1].include?("1") is false because 1 == "1" is false in Ruby. Symbols and strings are not interchangeable: [:draft].include?("draft") is also false.
  • NaN is never “in” the array. Float::NAN == Float::NAN is false, so include? will never find a NaN even when one is in the array:
    [1.0, Float::NAN, 2.0].include?(Float::NAN)  # => false
  • Custom classes need ==. The default Object#== is identity comparison, so two distinct instances with the same attributes are not equal unless the class overrides == (or includes Comparable and defines <=>):
    class Role
      include Comparable
      attr_reader :name
      def initialize(name) = @name = name
      def ==(other) = other.is_a?(Role) && other.name == name
      def <=>(other) = name <=> other.name
    end
    
    [Role.new("admin"), Role.new("owner")].include?(Role.new("admin"))  # => true

Performance

Array#include? is O(n). MRI implements the scan in C (rb_ary_include_p), so it skips the per-element Ruby method dispatch that Enumerable#include? would do, but it still has to walk the array element by element.

For repeated lookups against a stable collection, build a Set once and use set.include?(x) for O(1) checks. This turns the membership test from a linear search into a hash-backed lookup:

require "set"
ALLOWED = Set.new(%w[admin editor viewer])

ALLOWED.include?("admin")  # => true
ALLOWED.include?("owner")  # => false

Calling arr.include?(x) inside a loop that iterates arr is O(n²). Build the Set once at the top of the method, then look up against it. The threshold where Set starts winning depends on array size and lookup frequency, but a few hundred lookups against a few hundred elements is usually the point where the conversion pays for itself.

Common mistakes

Using include? on a hash and expecting a value check. Hash#include? tests for keys, not values. Use hash.value?(v) (or hash.has_value?(v)) to test whether a value is present. The two methods share a name in the standard library but have very different semantics, and the confusion is one of the most common gotchas in Ruby code review.

Passing a regex. include? does not accept a block. To test whether any element matches a pattern, use any? with a block, or grep if you want the matching elements back. The block form is also what you want when the search predicate is more complex than == — for example, matching by attribute, by a computed key, or by a substring check:

names = ["alice", "bob", "alex"]
names.any? { |n| n.start_with?("al") }   # => true
names.grep(/^al/)                          # => ["alice", "alex"]

Splats and “is any of these values in the array?” include? takes exactly one argument, so arr.include?(*[:a, :b]) becomes arr.include?([:a, :b]) and asks whether the array [:a, :b] is itself an element — almost never what the caller wanted. To test whether any of several values is present, intersect the two arrays and check whether the result is non-empty. The & operator is Array#intersection, which walks both arrays and returns the values they share; if the result is empty, none of the required values is in the source, and if non-empty, at least one of them is. This form is often the clearest way to phrase the membership question because the code reads like the question you are actually asking.

roles = %w[admin editor viewer]
(roles & %w[admin owner]).any?  # => true

Or test every required value explicitly with all?. Use all? when the requirement is that all of the values must be present — think “must have every one of these” — and use intersection.any? when the requirement is that at least one of them must be present — think “must have at least one of these”. The two forms read differently and answer different questions, so pick the one whose shape matches the requirement you are encoding:

required = %w[admin editor]
required.all? { |r| roles.include?(r) }  # => true

See Also

  • Array#any? — block-form predicate when you need more than ==.
  • Array#find — returns the matching element instead of a boolean.
  • Hash#include? — same name, different semantics (keys only).