Enumerable#take_while
Array · Updated March 31, 2026 · Enumerable The take_while method returns elements from the beginning of a collection as long as the condition evaluates to true. The moment the condition returns false, iteration stops — no further elements are checked.
How It Works
collection.take_while { |element| condition }
take_while passes each element to the block in order. While the block returns true, elements are collected. The first false result halts iteration and take_while returns what it has collected so far. Elements after the failure point are never evaluated.
Basic Usage
Take numbers while they are less than 10:
[1, 4, 7, 9, 12, 18].take_while { |n| n < 10 }
# => [1, 4, 7, 9]
Works with any enumerable, including ranges and lazy enumerables:
(1..20).take_while { |n| n < 5 }
# => [1, 2, 3, 4]
[1, 2, 3, 4, 5].lazy.take_while { |n| n < 3 }.to_a
# => [1, 2]
With strings:
words = ["apple", "banana", "cherry", "apricot", "blueberry"]
words.take_while { |w| w.start_with?("b") }
# => ["banana"]
take_while vs take
take and take_while look similar but behave differently:
| Method | Selection criteria | Stops when |
|---|---|---|
take(n) | Positional — first n elements | Exactly n elements have been collected |
take_while { block } | Conditional — block returns true | Block returns false for the first time |
# take(3) always returns 3 elements
[1, 2, 3, 4, 5].take(3)
# => [1, 2, 3]
# take_while stops on the first false
[1, 2, 3, 4, 5].take_while { |x| x < 3 }
# => [1, 2]
If take is given a number larger than the collection, it returns everything. If take_while never gets a false result, it also returns everything:
[1, 2].take(10)
# => [1, 2]
[1, 2].take_while { |x| x < 10 }
# => [1, 2]
Relationship to each
take_while is conceptually equivalent to manually breaking out of an each loop as soon as the condition fails:
# take_while
result = [1, 4, 7, 9, 12].take_while { |n| n < 10 }
# => [1, 4, 7, 9]
# Manual equivalent
result = []
[1, 4, 7, 9, 12].each do |n|
if n < 10
result << n
else
break
end
end
# => [1, 4, 7, 9]
The key difference is that take_while returns the collected elements directly, while the manual approach requires setting up an accumulator variable.
Practical Examples
Take Until a Sentinel Value
Process a collection up to a terminating marker:
data = ["header", "row1", "row2", "row3", "---", "more rows"]
data.take_while { |item| item != "---" }
# => ["header", "row1", "row2", "row3"]
This is useful for reading configuration lines until a divider, or processing log entries until a blank line.
Take First Matching Group
Extract the first consecutive group of elements sharing a property:
transactions = [100, -50, -30, 200, -10, 400, -20]
# Group of consecutive deposits (positive values)
transactions.take_while { |t| t > 0 }
# => [100]
# Reset: find the next group's start
remaining = transactions.drop_while { |t| t > 0 }
# => [-50, -30, 200, -10, 400, -20]
remaining.take_while { |t| t < 0 }
# => [-50, -30]
Skip Header Lines
Remove leading metadata from a document:
lines = ["# Config", "host: localhost", "---", "data: foo", "data: bar"]
lines.take_while { |line| !line.start_with?("---") }
# => ["# Config", "host: localhost"]
Filter Sorted Data
Since take_while stops at the first non-matching element, it is efficient on sorted collections:
scores = [95, 88, 76, 65, 55, 42]
# All scores above a threshold (stops immediately after first failure)
scores.take_while { |s| s >= 70 }
# => [95, 88, 76]
take_while vs drop_while
take_while and drop_while are complementary — one keeps the leading elements, the other discards them:
| Method | Return value |
|---|---|
take_while { block } | Leading elements where block is true |
drop_while { block } | Trailing elements starting from first false |
data = [1, 3, 5, 6, 7, 9]
data.take_while(&:odd?) # => [1, 3, 5]
data.drop_while(&:odd?) # => [6, 7, 9]
Together they partition a collection at the first failure point:
data = [2, 4, 6, 8, 10, 12]
threshold = 7
leading = data.take_while { |x| x < threshold }
trailing = data.drop_while { |x| x < threshold }
puts "Below #{threshold}: #{leading.inspect}" # => [2, 4, 6]
puts "#{threshold} and above: #{trailing.inspect}" # => [8, 10, 12]
See Also
- /reference/enumerable/enumerable-take/ — return the first n elements
- /reference/enumerable/enumerable-drop-while/ — skip elements while condition is true
- /reference/enumerable/enumerable-each-slice/ — group elements into fixed-size chunks