Enumerable#include?

Updated April 1, 2026 · Enumerable
ruby enumerable include? stdlib

What does include? do?

include? checks whether the collection contains at least one element that equals the argument. It returns true if a match is found, false otherwise.

The method uses the == operator for comparison, not ===. This distinction matters when working with custom objects or specialized types like ranges.

Basic Usage

[1, 2, 3].include?(2)        # => true
[1, 2, 3].include?(5)        # => false
["apple", "banana"].include?("apple")  # => true

With a range:

(1..10).include?(5)          # => true
(1..10).include?(15)         # => false

With a string:

"hello".include?("lo")       # => true
"hello".include?("x")        # => false

== vs === Comparison

include? uses ==, not ===. This is a common source of confusion.

# Using == (what include? actually uses)
(1..5).include?(3)            # => true  (1 == 3 is false, but 3 == 3..5 is true via Range#==)

# What you might expect with ===
(1..5) === 3                  # => true  (Range#=== checks membership)
(1..5) === 7                  # => false

For most built-in types this difference is subtle, but it becomes important when you define custom equality semantics.

Performance Considerations

The performance of include? depends on the collection type:

# Each element is checked sequentially until a match is found
large_array = (1..1_000_000).to_a
large_array.include?(999_999)  # Slow — checks every element until the end
large_array.include?(1)        # Fast — finds it immediately

For large arrays, consider using a Set instead if you need frequent membership tests.

Sets — O(1) constant time

require "set"

large_set = Set.new(1..1_000_000)
large_set.include?(999_999)    # Fast — uses hash lookup
large_set.include?(1)          # Fast

Hashes — O(1) for key lookup

{ a: 1, b: 2 }.include?(:a)    # => true
{ a: 1, b: 2 }.include?(:c)    # => false

Ranges — O(1) for numeric ranges

# Ruby's Range#include? checks the bounds, not every element
(1..1_000_000).include?(500_000)  # => true

Using with Custom Objects

For include? to work with your own objects, your class must implement the == method:

class Task
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def ==(other)
    other.is_a?(Task) && name == other.name
  end
end

tasks = [Task.new("Write"), Task.new("Review")]
tasks.include?(Task.new("Write"))   # => true
tasks.include?(Task.new("Publish")) # => false

Without ==, include? falls back to identity comparison (same object in memory).

The member? Alias

include? has an alias called member?. They behave identically:

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

(10..20).include?(15)   # => true
(10..20).member?(15)    # => true

Use whichever reads better in context. member? can feel more natural when describing set membership:

roles.include?(:admin)    # => "Does this user have the admin role?"
roles.member?(:admin)     # => reads like set membership

Practical Examples

Checking for a value in a list

ALLOWED_EXTENSIONS = [".rb", ".rake", ".gemspec"]

def process_file(filename)
  ext = File.extname(filename)
  if ALLOWED_EXTENSIONS.include?(ext)
    puts "Processing #{filename}"
  else
    puts "Skipping #{filename} — disallowed extension"
  end
end

process_file("script.rb")    # => Processing script.rb
process_file("data.csv")    # => Skipping data.csv — disallowed extension

Guard clause pattern

def register(email)
  return if BLACKLISTED_EMAILS.include?(email)
  # ... registration logic
end

Conditional logic

valid_statuses = %i[draft published archived]

def update_status(id, new_status)
  if valid_statuses.include?(new_status)
    # update the record
  else
    raise ArgumentError, "Unknown status: #{new_status}"
  end
end

See Also