Collection::takeWhile
Returns items from the start until the callback first returns false, then stops iterating.
public function takeWhile(mixed $value): staticParameters
| Parameter | Type | Description |
|---|---|---|
$value | TValue|callable(TValue, TKey): bool | A callback receiving the item and its key and returning a boolean, or a plain value — in which case items are taken while they loosely equal that value. |
Return value
staticA new collection of the leading items that satisfied the condition, with their original keys preserved.
takeWhile walks the collection from the front and returns the leading run of items for which the callback stays truthy. The moment the callback returns false it stops and never looks at the rest.
When to reach for it
Whenever the collection is ordered and the condition is monotonic. On a list sorted by date, takeWhile(fn ($r) => $r['at'] < $cutoff) is both correct and cheap. On a parse buffer, it grabs the header block up to the first blank line. The short-circuit matters most on a LazyCollection reading a large file or a paginated API, where filter would drain the whole source.
What trips people up
The difference from filter is not cosmetic. filter tests every item and keeps all matches anywhere in the collection; takeWhile keeps only a prefix and discards later matches. On unordered data the two give different answers, and takeWhile is usually the wrong one.
If the callback never returns false, you get the entire collection — there is no "not found" signal. Keys are preserved, so add values() before encoding to JSON. And because the value form is detected with a callable check that excludes strings, passing a function name as a string compares against that string instead of calling it.
Example
When to use it
- Read the header block of a file up to the first blank line
- Collect entries from a date-sorted log until you pass a cutoff timestamp
- Consume a lazy stream only until a running total exceeds a budget
- Take the leading segment of a sorted list that is still under a threshold
Frequently asked questions
What is the difference between takeWhile and filter in Laravel?
takeWhile returns only the leading run of matching items and stops at the first failure, while filter tests every item and keeps matches from anywhere in the collection. They agree only when the data is ordered so all matches come first.
What does takeWhile return if the condition is never false?
The entire collection. There is no separate "not found" signal, so compare the result's count against the source's count if you need to know whether the condition ever tripped.
Does takeWhile stop reading a LazyCollection early?
Yes. It is built on takeUntil, which stops pulling from the underlying generator as soon as the condition fails, so it is safe on an infinite or very large lazy source.
More examples
Parsing a header block
takeWhile finds the header run without a manual index or a break statement, and its count then tells skip() where the body begins (the +1 steps over the blank separator). Note the values() calls: takeWhile and skip both preserve the source keys.
<?php
$lines = collect([
'Subject: Invoice 4021',
'From: [email protected]',
'Date: 2024-03-01',
'',
'Hello,',
'Your invoice is attached.',
]);
$headers = $lines->takeWhile(fn (string $line) => $line !== '');
$body = $lines->skip($headers->count() + 1);
print_r($headers->values()->all());
echo '---', "\n";
print_r($body->values()->all());
// Output:
// Array ( [0] => Subject: Invoice 4021 [1] => From: [email protected] [2] => Date: 2024-03-01 )
// ---
// Array ( [0] => Hello, [1] => Your invoice is attached. )Short-circuiting a lazy source
1+2+3+4+5 is 15 and adding 6 would reach 21, so the callback returns false on 6 and the generator is abandoned there. Calling filter() on this source would loop forever; takeWhile is what makes an unbounded stream safe to consume.
<?php
use Illuminate\Support\LazyCollection;
// An effectively endless generator.
$stream = LazyCollection::make(function () {
$n = 1;
while (true) {
yield $n++;
}
});
$running = 0;
$budget = $stream->takeWhile(function (int $n) use (&$running) {
$running += $n;
return $running <= 20;
});
print_r($budget->all());
// Output:
// Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 )Passing a value instead of a callback
When the argument is not a callable, takeWhile compares each item to it with ==. The second call therefore accepts the int 1, the string '1' and the boolean true before stopping at 0. print_r renders all three as 1, which is exactly why loose comparison here is easy to misread — prefer an explicit closure.
<?php
$flags = collect(['on', 'on', 'off', 'on']);
// Takes while each item loosely equals 'on'.
print_r($flags->takeWhile('on')->all());
// Loose comparison: 1 == '1' == true.
print_r(collect([1, '1', true, 0, 1])->takeWhile(1)->all());
// Output:
// Array ( [0] => on [1] => on )
// Array ( [0] => 1 [1] => 1 [2] => 1 )
Discussion