When to Use chunkWhile(): 5 examples
Mastering the ChunkWhile Method in Laravel Collections: 5 Practical Examples
April 10, 2023
1k ViewsOne powerful feature of Laravel is its Collections class, a versatile wrapper around PHP arrays, allowing developers to perform complex operations easily. In this article, we'll explore the chunkWhile()
method in Laravel Collections and provide five practical examples to illustrate its usage.
What is chunkWhile()
?
The chunkWhile()
method in Laravel Collections splits the original collection into multiple sub-collections based on a given condition. This method accepts a callback function as its argument, which compares adjacent items in the collection. When the callback returns false
, a new chunk begins.
What is the difference between chunk()
and chunkWhile()
In Laravel Collections, the chunk()
and chunkWhile()
methods are used to split the original collection into sub-collections. However, they operate based on different principles and use cases.
- The
chunk()
method divides the collection into fixed-size sub-collections, while thechunkWhile()
method divides the collection based on a condition provided by a callback function. - The
chunk()
method takes an integer argument for the desired size of each chunk, while thechunkWhile()
method takes a callable (callback function) as its argument. - The
chunk()
method is more suitable for dividing collections uniformly, while thechunkWhile()
method provides more flexibility and control over how the collection is split.
The chunk()
Method
The chunk()
method divides a collection into fixed-size sub-collections. It takes a single integer argument representing the desired chunk size. The method returns a new collection containing the chunks, where each chunk is a sub-collection.
$numbers = collect([1, 2, 3, 4, 5, 6, 7, 8, 9]);
$chunks = $numbers->chunk(3);
// Result: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
The chunkWhile()
Method
The chunkWhile()
method splits a collection into sub-collections based on a given condition provided by a callback function. The callback function compares adjacent items in the collection, and when the callback returns false
, a new chunk begins. This method allows more control over dividing the collection than the chunk()
method.
$numbers = collect([1, 3, 5, 2, 4, 6, 7, 9, 11]);
$groupedNumbers = $numbers->chunkWhile(function ($current, $next) {
return $current % 2 === $next % 2;
});
// Result: [[1, 3, 5], [2, 4, 6], [7, 9, 11]]
A Deep Dive on How chunkWhile()
Works
At a high level the chunkWhile()
method converts the collection to a LazyCollection
, processes it with it's own chunkWhile()
method, then converts it back to a regular collection.
When using
chunkWhile()
you get access to the next item in the collection.
class Collection {
/**
* Chunk the collection into chunks with a callback.
*
* @param callable(TValue, TKey, static<int, TValue>): bool $callback
* @return static<int, static<int, TValue>>
*/
public function chunkWhile(callable $callback)
{
return new static(
$this->lazy()->chunkWhile($callback)->mapInto(static::class)
);
}
/**
* Get a lazy collection for the items in this collection.
*
* @return \Illuminate\Support\LazyCollection<TKey, TValue>
*/
public function lazy()
{
return new LazyCollection($this->items);
}
}
Here's what's happening step by step:
The method accepts a callable $callback
as an argument. This callback will determine when to split the collection into chunks.
-
The
lazy()
method is called on the current collection$this
. This method creates a new instance of theLazyCollection
class, passing the current collection items$this->items
as an argument. TheLazyCollection
class allows for efficient memory usage and better performance, as it processes collection elements on demand rather than loading them all into memory at once. -
The
chunkWhile()
method is then called on theLazyCollection
instance. This method chunks the collection based on the provided callback. It returns anotherLazyCollection
instance containing the chunks. -
mapInto()
is called on the resulting LazyCollection. This method maps each chunk (a lazy collection) into an instance of the original Collection class (indicated bystatic::class
). Essentially, this step converts chunks from lazy collections back into the original collection type. -
Finally, a new instance of the original
Collection
class (indicated bynew static
) is created, and the mapped chunks (instances of the originalCollection
class) are passed as an argument. This new instance of theCollection
class containing the chunks is returned as the result of thechunkWhile()
method.
How the chunkWhile()
Method Works
In the chunkWhile()
method of the LazyCollection
class, the collection is divided into chunks based on the provided callback function. The method takes a callable $callback
as an argument and returns a new instance of LazyCollection
created by passing a generator function.
class LazyCollection {
/**
* Chunk the collection into chunks with a callback.
*
* @param callable(TValue, TKey, \Illuminate\Support\Collection<TKey, TValue>): bool $callback
* @return static<int, static<int, TValue>>
*/
public function chunkWhile(callable $callback)
{
return new static(function () use ($callback) {
$iterator = $this->getIterator();
$chunk = new Collection;
if ($iterator->valid()) {
$chunk[$iterator->key()] = $iterator->current();
$iterator->next();
}
while ($iterator->valid()) {
if (! $callback($iterator->current(), $iterator->key(), $chunk)) {
yield new static($chunk);
$chunk = new Collection;
}
$chunk[$iterator->key()] = $iterator->current();
$iterator->next();
}
if ($chunk->isNotEmpty()) {
yield new static($chunk);
}
});
}
}
Here's a step-by-step explanation of what's happening inside the generator function:
-
The iterator is obtained for the current lazy collection using the
getIterator()
method. -
A new
Collection
object called$chunk
is created. This will hold the items for the current chunk. -
If the iterator is valid (i.e., there are items in the collection), the first item is added to the
$chunk
collection using the iterator'skey()
as the key andcurrent()
as the value. The iterator then moves to the next item using thenext()
method. -
A while loop is used to iterate through the remaining items in the collection as long as the iterator is valid.
- The
$callback
function is called with the current item, its key, and the current$chunk
collection as arguments. If the callback returnsfalse
, it means the current chunk should end at this point. - When the callback returns
false
, the current chunk is yielded as a new instance ofLazyCollection
containing the items in the$chunk
. - After yielding the current chunk, a new empty
Collection
object is created for the next chunk.
-
If the callback does not return
false
, the current item is added to the$chunk
collection using the iterator'skey()
as the key andcurrent()
as the value. The iterator then moves to the next item using thenext()
method. -
After the while loop, if the last chunk
$chunk
is not empty, it is yielded as a new instance ofLazyCollection
.
1. Grouping Consecutive Odd and Even Numbers
Suppose we have an array of numbers and want to group consecutive odd and even numbers into separate chunks. We can use the chunkWhile()
method to achieve this:
$numbers = collect([1, 3, 5, 2, 4, 6, 7, 9, 11]);
$groupedNumbers = $numbers->chunkWhile(function ($current, $next) {
return $current % 2 === $next % 2;
});
// Result: [[1, 3, 5], [2, 4, 6], [7, 9, 11]]
Here is how it works:
-
The
collect()
function is used to create a new collection instance from the array of numbers[1, 3, 5, 2, 4, 6, 7, 9, 11]
. -
The
chunkWhile()
method is called on the collection. This method groups the items in the collection into smaller collections based on a condition that is defined as a callback function. -
The callback function passed to
chunkWhile()
takes two parameters,$current
and$next
, which represent the current and next elements in the iteration. The callback returnstrue
if the current element and the next element have the same parity (even or odd), andfalse
otherwise. -
The result of the
chunkWhile()
method is assigned to the$groupedNumbers
variable. It is an array of collections, where each collection contains items that satisfy the condition passed to the callback function. -
In this example, the result is
[[1, 3, 5], [2, 4, 6], [7, 9, 11]]
, which means that the items in the original array were grouped into three collections. The first collection contains all the odd numbers, the second collection contains all the even numbers, and the third collection contains all the odd numbers again.
2. Separating Ascending Subsequences
Let's say we have an array of integers and want to separate it into chunks with ascending subsequences. We can use the chunkWhile()
method to do this:
$integers = collect([3, 5, 1, 2, 3, 8, 6, 7]);
$ascendingSubsequences = $integers->chunkWhile(function ($current, $next) {
return $current < $next;
});
// Result: [[3, 5], [1, 2, 3, 8], [6, 7]]
Let's break down the code step by step:
- The
collect()
function creates a new Laravel Collection instance from the given array of integers:
$integers = collect([3, 5, 1, 2, 3, 8, 6, 7]);
- The
chunkWhile()
method is called on the$integers
collection, which takes a callback function as an argument. This callback function compares adjacent items in the collection to determine when to create a new chunk. The callback function receives two parameters:$current
and$next
, representing the current item and the next item in the collection, respectively:
$ascendingSubsequences = $integers->chunkWhile(function ($current, $next) {
return $current < $next;
});
- Inside the callback function, the code checks whether
$current
is less than$next
. If the condition istrue
, it means that the current item and the next item form an ascending subsequence. In this case, thechunkWhile()
method continues to compare the next pair of adjacent items. If the condition isfalse
, a new chunk begins, and the comparison process continues with the next item in the collection. - After iterating through the entire collection, the
chunkWhile()
method returns a new collection containing sub-collections (chunks) of ascending subsequences.
In this specific example, the input array is [3, 5, 1, 2, 3, 8, 6, 7]
. The chunkWhile()
method processes the array and creates chunks of ascending subsequences: [[3, 5], [1, 2, 3, 8], [6, 7]]
.
3. Grouping Items by Similarity
Imagine we have an array of strings and want to group them based on their length. We can use the chunkWhile()
method to create chunks of strings with the same length:
$strings = collect(['one', 'two', 'three', 'four', 'five', 'six', 'seven']);
$groupedStrings = $strings->chunkWhile(function ($current, $next) {
return strlen($current) === strlen($next);
});
// Result: [['one', 'two'], ['three', 'four', 'five', 'six'], ['seven']]
Let's break down the code step by step to understand how it works.
1. Create a Laravel Collection from an array of strings
$strings = collect(['one', 'two', 'three', 'four', 'five', 'six', 'seven']);
Here, we create a new Laravel Collection named $strings
that contains seven elements, each being a string representing a number.
2. Apply the chunkWhile()
method to the $strings
collection
$groupedStrings = $strings->chunkWhile(function ($current, $next) {
return strlen($current) === strlen($next);
});
The chunkWhile()
method takes a callback function as its argument. This callback function is executed for each pair of adjacent elements in the collection, with $current
representing the current element and $next
representing the next element.
The callback function checks whether the length of the current and next strings is the same using strlen($current) === strlen($next)
. If this condition is true
, the current and next strings will be grouped together in the same chunk. If the condition is false
, a new chunk will be created, starting with the next string.
3. Result
// Result: [['one', 'two'], ['three', 'four', 'five', 'six'], ['seven']]
The resulting $groupedStrings
collection contains three sub-collections:
- The first sub-collection contains the strings
'one'
and'two'
, both having 3 characters. - The second sub-collection contains the strings
'three'
,'four'
,'five'
, and'six'
, each having 5 characters. - The third sub-collection contains the string
'seven'
, having 5 characters.
4. Grouping Consecutive Dates
If we have an array of dates and want to group consecutive days, we can use the chunkWhile()
method to create chunks of consecutive dates:
use Carbon\Carbon;
$dates = collect([
Carbon::parse('2023-01-01'),
Carbon::parse('2023-01-02'),
Carbon::parse('2023-01-03'),
Carbon::parse('2023-01-05'),
Carbon::parse('2023-01-06'),
]);
$consecutiveDates = $dates->chunkWhile(function ($current, $next) {
return $current->addDay()->equalTo($next);
});
// Result: [['2023-01-01', '2023-01-02', '2023-01-03'], ['2023-01-05', '2023-01-06']]
Let's break down the code to understand how it works:
- The
$dates
variable is assigned a Laravel Collection created using thecollect()
function. The collection contains five Carbon objects representing dates, which are created using theCarbon::parse()
method. This method accepts a date string and returns a Carbon object.
-
The
chunkWhile()
method is called on the$dates
collection, and it takes a callback function as its argument. The callback function accepts two parameters:$current
and$next
, which represent adjacent items in the collection. -
Inside the callback function, the
addDay()
method is called on the$current
Carbon object. This method returns a new Carbon instance with the date incremented by one day. Then, theequalTo()
method is called on the incremented Carbon object to compare it with the$next
Carbon object. If both objects represent the same date, the method returnstrue
; otherwise, it returnsfalse
. -
The
chunkWhile()
method splits the original$dates
collection into sub-collections (chunks) based on the callback function's return value. When the callback returnsfalse
, it starts a new chunk. In this case, the chunks are created for consecutive dates. -
The final result is a collection of two sub-collections (chunks), where the first chunk contains three consecutive dates (
'2023-01-01', '2023-01-02', '2023-01-03'
), and the second chunk contains two consecutive dates ('2023-01-05', '2023-01-06'
).
5. Grouping Objects by a Common Property
Consider an array of objects representing employees, and we want to group them by their job title. We can use the chunkWhile()
method to create chunks of employees with the same job title:
class Employee {
public $name;
public $jobTitle;
public function __construct($name, $jobTitle) {
$this->name = $name;
$this->jobTitle = $jobTitle;
}
}
$employees = collect([
new Employee('Alice', 'Developer'),
new Employee('Bob', 'Developer'),
new Employee('Eve', 'Manager'),
new Employee('Charlie', 'Developer'),
new Employee('David', 'Developer'),
]);
$groupedEmployees = $employees->chunkWhile(function ($current, $next) {
return $current->jobTitle === $next->jobTitle;
});
// Result: [[Alice (Developer), Bob (Developer)], [Eve (Manager)], [Charlie (Developer), David (Developer)]]
This code defines an Employee
class and then uses the chunkWhile()
method to group a collection of Employee
objects based on their job titles.
-
The
Employee
class is defined with two public properties:name
andjobTitle
. The constructor method__construct
takes two arguments,$name
and$jobTitle
, which are used to initialize the corresponding properties when an instance of the class is created. -
A new collection named
$employees
is created using the collect function. This collection contains fiveEmployee
objects, each initialized with a name and a job title. -
The
$employees
collection is then passed to thechunkWhile()
method. ThechunkWhile()
method accepts a callback function as an argument, which is responsible for determining if the current and next items in the collection should be grouped together. -
In this case, the callback function checks if the
jobTitle
property of the current and nextEmployee
objects are the same. If they are the same, the callback function returnstrue
, indicating that the two employees should be grouped together. Otherwise, it returnsfalse
, indicating that the employees should be placed in separate chunks. -
The
chunkWhile()
method iterates through the$employees
collection, applying the callback function to each pair of adjacent items, and creates a new collection of sub-collections based on the callback's return value. -
The final result is a collection of three sub-collections: the first contains two developers (Alice and Bob), the second contains one manager (Eve), and the third contains two more developers (Charlie and David).
Wrapping Up
The chunkWhile()
method in Laravel Collections is a powerful tool that allows developers to split collections into sub-collections based on a given condition. By using chunkWhile()
with appropriate callback functions, you can easily group, separate, and organize your collections to fit your application's needs. The five examples provided in this article demonstrate various practical use cases, from grouping consecutive numbers to organizing objects by a common property.