Enumerable#include?
Enumerable#include? is part of Ruby’s standard library behavior for collections, so you will see it on arrays, ranges, sets, and hashes. It answers a simple question: does this collection already contain the value I care about?
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.
That comparison rule is what makes the method feel familiar in Ruby code. You can use it for ordinary membership checks, then rely on == when you define your own matching logic for custom objects.
Basic Usage
[1, 2, 3].include?(2) # => true
[1, 2, 3].include?(5) # => false
["apple", "banana"].include?("apple") # => true
The examples above cover the three collection types you are most likely to call include? on in everyday Ruby: arrays for list membership, ranges for numeric bounds checks, and strings for substring detection. Each uses == under the hood, but the practical meaning differs across types in ways worth understanding.
With a range:
(1..10).include?(5) # => true
(1..10).include?(15) # => false
Range include? checks whether the argument falls within the bounds by comparing it against the start and end values. For numeric ranges, this is a constant-time operation — Ruby does not iterate through every integer between 1 and 10. That makes (1..1_000_000).include?(value) fast regardless of the range size.
With a string:
"hello".include?("lo") # => true
"hello".include?("x") # => false
String include? checks for a contiguous substring — it does not test individual characters. The call "hello".include?("lo") returns true because “lo” appears as a consecutive run in the source string, while "hello".include?("eo") would return false even though both letters are present individually.
== 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
The distinction matters most when you work with objects that define both == (value equality) and === (pattern matching) differently — ranges are the classic example. Range#=== checks set membership, while Range#== compares range objects themselves. Because include? delegates to ==, passing a range argument to include? on another range tests whether the ranges are equal objects, not whether the argument falls within the bounds.
Performance considerations
The performance of include? depends on the collection type:
Arrays — O(n) linear search
# 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
The linear nature of array scanning means include? performance gets worse as the array grows and the match sits near the end. If your code calls include? repeatedly on the same large array inside a loop, the cumulative cost can add up quickly. In those cases, converting to a different data structure is often the better fix than trying to optimize the scan itself.
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
A Set stores its elements in a hash table under the hood, so include? on a set runs in constant time regardless of how many elements are present. The trade-off is memory: a set uses more space than a plain array because of the hash buckets. For code that needs to check membership many times against the same collection, that trade-off is almost always worth it.
Hashes — O(1) for key lookup
{ a: 1, b: 2 }.include?(:a) # => true
{ a: 1, b: 2 }.include?(:c) # => false
Hash include? checks keys, not values. The call { a: 1, b: 2 }.include?(:a) returns true because :a is a key, but { a: 1, b: 2 }.include?(1) returns false because 1 is a value, not a key. If you need to check for a value, use hash.value?(val) or iterate with hash.any? { |k, v| v == target }.
Ranges — O(1) for numeric ranges
# Ruby's Range#include? checks the bounds, not every element
(1..1_000_000).include?(500_000) # => true
Range include? uses a bounds comparison rather than iterating over every element. For a numeric range, Ruby checks whether the argument is greater than or equal to the start and less than or equal to the end. That means the size of the range does not affect the lookup time — a million-element range and a three-element range are equally fast.
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 a custom ==, include? falls back to BasicObject#==, which checks object identity — two Task.new("Write") instances would be considered different even though they carry the same name. Defining == is therefore a deliberate choice: it tells Ruby that two objects with the same content should be treated as equivalent for collection membership purposes.
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, while include? is the version most developers reach for first. Both call the same code path, so the choice is purely about readability for the person maintaining the code later.
roles.include?(:admin) # => "Does this user have the admin role?"
roles.member?(:admin) # => reads like set membership
Both lines produce the same result, but the question each asks the reader is slightly different. The first asks “does the roles array include admin?” and the second asks “is admin a member of the roles set?” Pick the one that matches the mental model your code is already using.
Practical Examples
These examples show include? in different contexts, from validating a file extension to guarding a method and checking a list of valid states. In each case, the method keeps the branch readable without hiding the actual comparison.
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
The extension check is a classic include? pattern: define a list of valid values as a constant, then use the method to branch on whether the input appears in that list. The constant keeps the allowed set visible near the top of the file, and the include? call keeps the branch short and declarative.
Guard clause pattern
def register(email)
return if BLACKLISTED_EMAILS.include?(email)
# ... registration logic
end
The guard clause pattern pairs well with include? because it reads as a natural sentence: “return early if the blacklist includes this email.” The method exits without leaving any state to clean up, and the remaining code can assume the email passed the check without carrying an explicit flag.
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
- /reference/enumerable/enumerable-tally/ — Count occurrences of each element
- /reference/enumerable/enumerable-group-by/ — Group elements by a condition
- /reference/enumerable/enumerable-reduce/ — Reduce an enumerable to a single value