Enumerable#take_while

Returns: Array · Updated March 31, 2026 · Enumerable
ruby enumerable take_while stdlib

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:

MethodSelection criteriaStops when
take(n)Positional — first n elementsExactly n elements have been collected
take_while { block }Conditional — block returns trueBlock 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:

MethodReturn 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