Последняя функция из нашей тройки — array_reduce или свертка. В других языках она называется accumulate
или fold
. Эта функция используется для агрегации. Она устроена немного сложнее, чем map
и filter
, но сохраняет общий подход с передачей функции.
Как обычно, начнем с примера на циклах. Реализуем код, находящий самого взрослого пользователя:
<?php
$users = [
['name' => 'Igor', 'age' => 19],
['name' => 'Danil', 'age' => 4],
['name' => 'Vovan', 'age' => 4],
['name' => 'Matvey', 'age' => 16],
];
$oldest = $users[0];
foreach ($users as $user) {
if ($user['age'] > $oldest['age']) {
$oldest = $user;
}
}
print_r($oldest); // => ['name' => 'Igor', 'age' => 19]
Основное отличие агрегации от отображения и фильтрации заключается в том, что результатом агрегации может быть любой тип данных — как примитивный, так и составной. Например, результатом может быть массив. Кроме того, агрегация нередко подразумевает инициализацию начальным значением. В примере выше она выполняется на строчке $oldest = $users[0];
.
Посмотрим еще один пример агрегации — группировку имен пользователей по возрасту:
<?php
$users = [
['name' => 'Petr', 'age' => 4],
['name' => 'Igor', 'age' => 19],
['name' => 'Vovan', 'age' => 4],
['name' => 'Matvey', 'age' => 16],
];
$usersByAge = [];
foreach ($users as $user) {
$usersByAge[$user['age']][] = $user['name'];
}
print_r($usersByAge);
# => Array
# (
# [4] => Array
# (
# [0] => Petr
# [1] => Vovan
# )
#
# [19] => Array
# (
# [0] => Igor
# )
#
# [16] => Array
# (
# [0] => Matvey
# )
#
# )
В этом примере результатом агрегации становится массив массивов, который в самом начале инициируется пустым массивом. Значение, которое накапливает результат агрегации, принято называть словом аккумулятор. В примерах выше это $oldest
и $usersByAge
.
Реализуем первый пример, используя array_reduce
:
<?php
$oldest = array_reduce($users, function ($acc, $user) {
return $user['age'] > $acc['age'] ? $user : $acc;
}, $users[0]);
print_r($oldest); // => ['name' => 'Igor', 'age' => 19]
Функция array_reduce
принимает на вход три параметра. Два из них уже традиционны — это коллекция и функция-обработчик, а вот третьим выступает начальное значение аккумулятора. Поиск самого взрослого пользователя аналогичен поиску максимального или минимального числа в массиве. Соответственно, аккумулятор должен быть инициализирован первым пользователем. Этот же аккумулятор возвращается наружу.
Колбэк-функция, передаваемая в array_reduce
— это самая важная часть и ключ к пониманию работы всего механизма агрегации. Она принимает на вход два значения:
- Текущее значение аккумулятора
- Текущий обрабатываемый элемент
Задача функции — вернуть новое значение аккумулятора. При этом array_reduce
никак не анализирует содержимое аккумулятора. Все, что она делает — она передает значение аккумулятора в каждый новый вызов, пока не будет обработана вся коллекция, и в конце концов вернет его наружу. Подчеркнем, что возвращать аккумулятор надо всегда, даже если он не изменился.
Второй пример с использованием array_reduce
выглядит так:
<?php
$usersByAge = array_reduce($users, function ($acc, $user) {
$acc[$user['age']][] = $user['name'];
return $acc;
}, []);
print_r($usersByAge);
Код практически не изменился. Как видите, ушел цикл и появился возврат аккумулятора из анонимной функции.
Перейдем к реализации:
<?php
function myReduce($coll, callable $callback, $init = null)
{
$acc = $init;
foreach ($coll as $item) {
$acc = $callback($acc, $item); // Заменяем старый аккумулятор новым
}
return $acc;
}
Функция array_reduce
очень мощная. Формально, можно работать, используя только ее, потому что она может заменить отображение и фильтрацию. Но делать так не стоит. Агрегация управляет состоянием (аккумулятором) явно. Такой код всегда сложнее и требует больше действий. Поэтому, если задачу возможно решить отображением или фильтрацией, то так и нужно делать.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.