Удаляем элемент из массива PHP по значению: под капотом

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

PHP array delete by value (not key): заглядываем под капот

Представьте: вы пишете скрипт на PHP, обрабатываете список товаров, и вдруг нужно удалить из массива позицию с конкретной ценой или названием. Вы точно знаете значение — например, "яблоко" — но понятия не имеете, на каком индексе оно лежит. Знакомая ситуация? Если да, то вы не одиноки: вопрос "PHP array delete by value (not key)" собирает тысячи просмотров на Stack Overflow. И это не случайно.

В PHP нет встроенной функции array_remove_by_value(). Поэтому разработчики выкручиваются: кто-то пишет цикл, кто-то использует array_search в паре с unset, кто-то применяет array_diff или array_filter. Но что происходит внутри? Какой способ быстрее? И почему один и тот же код может работать по-разному в разных версиях PHP?

В этой статье мы не просто дадим готовое решение — мы заглянем под капот каждого подхода, разберём его производительность и подводные камни. А ещё покажем реальный кейс из жизни бэкенд-разработчика. Поехали!

Классический способ: array_search + unset

Самый популярный ответ на Stack Overflow выглядит так:

<?php
$array = ['яблоко', 'груша', 'апельсин', 'груша'];
$value = 'груша';

if (($key = array_search($value, $array)) !== false) {
    unset($array[$key]);
}
?>

Этот код находит первое вхождение значения и удаляет его. Но давайте разберёмся, что тут происходит на самом деле.

Как работает array_search

array_search() — это линейный поиск. PHP проходит по массиву от начала до конца, сравнивая каждое значение с искомым. Сравнение по умолчанию — нестрогое (==), то есть 1 и '1' считаются равными. Если нужно строгое сравнение (===), передайте третий параметр true:

<?php
$array = [0, '0', 1];
$key = array_search('0', $array, true); // вернёт 1 (строка '0'), а не 0
?>

Если значение не найдено, функция возвращает false. Поэтому проверка !== false обязательна — иначе unset($array[0]) удалит первый элемент, если значение окажется с ключом 0.

Подводный камень: unset не переиндексирует массив

После unset массив остаётся ассоциативным — числовые ключи не сдвигаются. Если вам нужен плотный массив с последовательными индексами, используйте array_values():

<?php
$array = ['яблоко', 'груша', 'апельсин'];
unset($array[1]);
$array = array_values($array); // ['яблоко', 'апельсин'] с ключами 0 и 1
?>

Но помните: array_values() создаёт новый массив, что увеличивает потребление памяти.

Удаление всех вхождений: array_diff

Если нужно удалить все элементы с заданным значением, array_search в цикле — не лучшая идея. Намного элегантнее работает array_diff():

<?php
$array = ['яблоко', 'груша', 'апельсин', 'груша'];
$array = array_diff($array, ['груша']);
// Результат: ['яблоко', 'апельсин'] (ключи сохраняются)
?>

Функция возвращает новый массив, из которого удалены все элементы, совпадающие со значениями из второго массива. Внутри PHP использует хеш-таблицу для второго массива, поэтому операция выполняется за O(n + m), где n — размер первого массива, m — размер второго.

Важно: array_diff сравнивает значения как строки ((string) $elem1 === (string) $elem2). Для смешанных типов это может дать неожиданный результат:

<?php
$array = [0, '0', false];
$result = array_diff($array, [0]);
// Результат: [] — все три элемента преобразуются в пустую строку и совпадают!
?>

Поэтому для строгой типизации лучше использовать array_filter с callback.

Гибкое удаление: array_filter

array_filter() — это швейцарский нож для фильтрации массивов. Он позволяет удалять элементы по любому условию:

<?php
$array = ['яблоко', 'груша', 'апельсин', 'груша'];
$array = array_filter($array, function($item) {
    return $item !== 'груша';
});
// Результат: ['яблоко', 'апельсин'] (ключи сохраняются)
?>

Если нужно сбросить ключи, добавьте array_values():

$array = array_values(array_filter($array, fn($item) => $item !== 'груша'));

Производительность array_filter

array_filter проходит по массиву ровно один раз и применяет callback к каждому элементу. Сложность — O(n). На массивах до 10 000 элементов разница между array_filter и array_diff незаметна. Но на больших объёмах (100 000+) array_diff может быть быстрее, так как не вызывает пользовательскую функцию для каждого элемента.

Сравнение производительности: бенчмарк

Давайте проверим три подхода на массиве из 100 000 элементов (строки длиной 10 символов). Удаляем одно значение, которое встречается один раз в середине массива.

<?php
// Генерация тестового массива
$array = [];
for ($i = 0; $i < 100000; $i++) {
    $array[] = bin2hex(random_bytes(5));
}
$array[50000] = 'target';

// 1. array_search + unset
$start = microtime(true);
$key = array_search('target', $array);
if ($key !== false) unset($array[$key]);
echo 'array_search+unset: ' . (microtime(true) - $start) . ' сек' . PHP_EOL;

// 2. array_diff
$array2 = $array; // копия для честности
$start = microtime(true);
$array2 = array_diff($array2, ['target']);
echo 'array_diff: ' . (microtime(true) - $start) . ' сек' . PHP_EOL;

// 3. array_filter
$array3 = $array;
$start = microtime(true);
$array3 = array_filter($array3, fn($v) => $v !== 'target');
echo 'array_filter: ' . (microtime(true) - $start) . ' сек' . PHP_EOL;
?&                

Блоги

Рекомендаций по книгам