Содержание
Задача
У тебя есть массив автомобилей в таком виде:
$cars = [
['brand' => 'Ford', 'model' => 'Mustang', 'year' => 2020],
['brand' => 'Ford', 'model' => 'Fusion', 'year' => 2018],
['brand' => 'Ford', 'model' => 'Mustang', 'year' => 2020],
['brand' => 'Toyota', 'model' => 'Corolla', 'year' => 2019],
['brand' => 'Ford', 'model' => 'Escape', 'year' => 2021],
['brand' => 'Toyota', 'model' => 'Camry', 'year' => 2018],
];
Твоя цель:
- ❌ Удали дубликаты машин с одинаковыми
'model'и'year'. - 🔎 Оставь только автомобили Ford.
- 🔄 Преобразуй каждый оставшийся элемент в строку такого формата:
"Model: Mustang (2020)". - 📦 Верни результат как массив строк.
Пример результата:
[
"Model: Mustang (2020)",
"Model: Fusion (2018)",
"Model: Escape (2021)"
]
Мои ошибки
<?php
$cars = [
['brand' => 'Ford', 'model' => 'Mustang', 'year' => 2020],
['brand' => 'Ford', 'model' => 'Fusion', 'year' => 2018],
['brand' => 'Ford', 'model' => 'Mustang', 'year' => 2020],
['brand' => 'Toyota', 'model' => 'Corolla', 'year' => 2019],
['brand' => 'Ford', 'model' => 'Escape', 'year' => 2021],
['brand' => 'Toyota', 'model' => 'Camry', 'year' => 2018],
];
// Удали дубликаты машин с одинаковыми 'model' и 'year'.
$result = array_reduce($cars, function($newArr, $car) {
if (!in_array($car['model'], $newArr) || !in_array($car['model'], $newArr)) {
$newArr[] = $car;
}
}, []);
print_r($result);
ошибка:
Fatal error: Uncaught TypeError: in_array(): Argument #2 ($haystack) must be of type array, null given in /home/user/scripts/code.php:15
Stack trace:
#0 /home/user/scripts/code.php(15): in_array('Fusion', NULL)
#1 [internal function]: {closure}(NULL, Array)
#2 /home/user/scripts/code.php(14): array_reduce(Array, Object(Closure), Array)
#3 {main}
thrown in /home/user/scripts/code.php on line 15
❌ Ошибки:
- Внутри
array_reduceты не возвращаешь результат в конце функции — безreturn $newArrничего не будет сохраняться. in_array($car['model'], $newArr)— это неправильно, потому что$newArrсодержит массивы, а ты сравниваешь его со строкой ('Mustang'и т.д.).- Ты сравниваешь только
model, но по задаче нужно сравнивать паруmodel+year.
in_array($car[‘model’], …) — ❌ не сработает с массивом массивов
Ты сравниваешь строку ($car['model']) со списком массивов, а in_array не умеет искать 'Mustang' в массиве типа:
[
['model' => 'Mustang'],
['model' => 'Fusion']
]
in_array("abc", ["abc", "def"]) → true
in_array отлично сравнивает строку со строками, число с числами, булевое с булевым и т.д.
Но! — in_array(“abc”, [[“model” => “abc”]]) → false
Если ты сравниваешь строку с массивами — in_array не будет “залазить внутрь” массивов:
$car = "Mustang";
$arr = [
["model" => "Mustang"],
["model" => "Fusion"]
];
in_array($car, $arr); // ❌ false
Потому что in_array сравнивает **$car == [“model” => “Mustang”]**, а это сравнение строки с массивом → false`.
🔧 Чтобы сравнить "Mustang" с ['model' => 'Mustang'] внутри массива:
Используй array_column() (читай здесь):
in_array("Mustang", array_column($arr, 'model')) // ✅ true
Если мы хотим использовать in_array, то надо сделать так чтобы мы сравнивали строки.
Поэтому Делаем ключ для сравнения model + year
Ты хочешь сравнивать дубликаты по model и year, значит нужен уникальный ключ, например:
$key = $car['model'] . '_' . $car['year'];
Но in_array($key, $newArr) не сработает, потому что $newArr — массив машин (не строк), а ты сравниваешь со строкой.
Решение: введем вспомогательный массив $seenKeys, чтобы в нём хранить строки вроде Mustang_2020.
Но array_reduce принимает только один аккумулятор. Значит мы должны хранить и $newArr, и $seenKeys в одном массиве.
Используем аккумулятор с двумя массивами
$result = array_reduce($cars, function($acc, $car) {
$key = $car['model'] . '_' . $car['year'];
if (!in_array($key, $acc['seen'])) {
$acc['seen'][] = $key;
$acc['filtered'][] = $car;
}
return $acc;
}, ['seen' => [], 'filtered' => []]);
Получаем финальный результат
После array_reduce, результат будет в $result['filtered'].
print_r($result['filtered']);
Итоговый код
<?php
$cars = [
['brand' => 'Ford', 'model' => 'Mustang', 'year' => 2020],
['brand' => 'Ford', 'model' => 'Fusion', 'year' => 2018],
['brand' => 'Ford', 'model' => 'Mustang', 'year' => 2020],
['brand' => 'Toyota', 'model' => 'Corolla', 'year' => 2019],
['brand' => 'Ford', 'model' => 'Escape', 'year' => 2021],
['brand' => 'Toyota', 'model' => 'Camry', 'year' => 2018],
];
$result = array_reduce($cars, function($acc, $car) {
$key = $car['model'] . '_' . $car['year'];
if (!in_array($key, $acc['seen'])) {
$acc['seen'][] = $key;
$acc['filtered'][] = $car;
}
return $acc;
}, ['seen' => [], 'filtered' => []]);
print_r($result['filtered']);
Мое полное решение
<?php
$cars = [
['brand' => 'Ford', 'model' => 'Mustang', 'year' => 2020],
['brand' => 'Ford', 'model' => 'Fusion', 'year' => 2018],
['brand' => 'Ford', 'model' => 'Mustang', 'year' => 2020],
['brand' => 'Toyota', 'model' => 'Corolla', 'year' => 2019],
['brand' => 'Ford', 'model' => 'Escape', 'year' => 2021],
['brand' => 'Toyota', 'model' => 'Camry', 'year' => 2018],
];
$result = array_reduce($cars, function($newArr, $car) {
if ($car['brand'] !== 'Ford') return $newArr;
$key = $car['model'] . '_' . $car['year'];
if (!in_array($key, $newArr["keys"])) {
$newArr["keys"][] = $key;
$newArr["filtered"][] = $car;
}
return $newArr;
}, ["keys" => [], "filtered" => []]);
$filtered_arr = $result["filtered"];
$final = array_map(function($item) {
return 'Model: ' . $item['model'] . '(' . $item['year'] . ')';
}, $filtered_arr);
// Вывод
Array
(
[0] => Model: Mustang(2020)
[1] => Model: Fusion(2018)
[2] => Model: Escape(2021)
)
Как улучшить мой код
Раздели фильтрацию и удаление дубликатов
Сейчас всё происходит в одном array_reduce. Это ок, но можно упростить, сделав два прохода: сначала фильтруем только Ford, потом убираем дубликаты. Код станет проще для чтения.
// 1. Оставим только Ford
$only_fords = array_filter($cars, fn($car) => $car['brand'] === 'Ford');
// 2. Удалим дубликаты по model + year
$seen = [];
$unique_fords = [];
foreach ($only_fords as $car) {
$key = $car['model'] . '_' . $car['year'];
if (!in_array($key, $seen)) {
$seen[] = $key;
$unique_fords[] = $car;
}
}
// 3. Преобразуем в строки
$formatted = array_map(
fn($car) => "Model: {$car['model']}({$car['year']})",
$unique_fords
);
print_r($formatted);
🧠 Альтернатива с array_reduce (если хочешь компактнее)
Если тебе всё-таки нравится array_reduce, можно просто чуть привести в порядок:
$result = array_reduce($cars, function($acc, $car) {
if ($car['brand'] !== 'Ford') return $acc;
$key = $car['model'] . '_' . $car['year'];
if (!in_array($key, $acc['seen'])) {
$acc['seen'][] = $key;
$acc['filtered'][] = $car;
}
return $acc;
}, ['seen' => [], 'filtered' => []]);
$formatted = array_map(
fn($car) => "Model: {$car['model']}({$car['year']})",
$result['filtered']
);
print_r($formatted);
********
Используем сокращённый синтаксис для анонимных функций в PHP, появившийся с версии PHP 7.4. Он называется Arrow Function (стрелочная функция), и это аналог function(...) { ... }, но компактнее.
Идеально подходит для array_map, array_filter, usort и т.д.
// Обычная функция
$double = function($x) {
return $x * 2;
};
// Стрелочная
$double = fn($x) => $x * 2;
Где будет Return.
Вариант 1.
$result = array_reduce($cars, function($acc, $car) {
$key = $car['model'] . '_' . $car['year'];
if (!in_array($key, $acc['seen'])) {
$acc['seen'][] = $key;
$acc['filtered'][] = $car;
}
return $acc;
}, ['seen' => [], 'filtered' => []]);
💡 Как работает:
- В каждой итерации функция всегда возвращает
$acc. - Даже если
ifне выполнился,array_reduceполучит обновлённый$acc.
✅ Это правильно и безопасно.
Вариант 2.
$result = array_reduce($cars, function($acc, $car) {
$key = $car['model'] . '_' . $car['year'];
if (!in_array($key, $acc['seen'])) {
$acc['seen'][] = $key;
$acc['filtered'][] = $car;
return $acc;
}
}, ['seen' => [], 'filtered' => []]);
Что произойдёт:
- Если условие
ifНЕ выполнится, то функция ничего не вернёт. - А
array_reduceожидает, что твоя функция всегда возвращает новый аккумулятор. - В результате PHP передаст
nullв следующую итерацию как$acc, и это вызовет ошибку:
TypeError: Cannot access offset of type null

Вывод:
Возврат return $acc | Что будет? |
|---|---|
| ✅ всегда в конце функции | всё работает |
❌ только внутри if | может сломаться, если условие не выполнится |
