Уникальные элементы массива: как избавиться от повторений в PHP
Методы фильтрации дубликатов в массивах PHP
Как получить массив, содержащий только уникальные значения, с помощью встроенной функции?
Наиболее простым и быстрым решением для одномерных массивов является функция array_unique(). Она принимает массив и возвращает новый массив, где каждое значение встречается один раз. При этом ключи первого вхождения сохраняются.
$arr = [1, 2, 2, 3, 'a', 'a'];
$unique = array_unique($arr);
// Результат: [0=>1, 1=>2, 3=>3, 4=>'a']Php уникальный массив (получение уникальных значений массива в php)
По умолчанию сравнение элементов происходит с приведением типов (SORT_REGULAR). Можно задать флаг для строгого сравнения: SORT_NUMERIC (числовое), SORT_STRING (строковое) или SORT_LOCALE_STRING.
Типичная ошибка: array_unique не работает с многомерными массивами. Если передать массив, содержащий подмассивы, возникнет Warning: "Array to string conversion". Решение – использовать рекурсивную сериализацию (см. варианты ниже).
Цель: быстро получить уникальные значения из одномерного массива с сохранением ключей. Случаи использования: обработка списков ID, тегов, названий.
Как получить уникальные значения с подсчётом количества повторений?
Функция array_count_values() считает вхождения каждого элемента, возвращая ассоциативный массив, где ключами являются значения исходного массива, а значениями – количество повторений. Чтобы получить только уникальные значения, применяется array_keys():
$arr = [1, 2, 2, 3, 1];
$counts = array_count_values($arr);
$unique = array_keys($counts);
// Результат: [0=>1, 1=>2, 2=>3]
Ограничение: array_count_values обрабатывает только строки и целые числа. Для float, null или массивов возникнет Warning. Решение: перед вызовом преобразовать все значения в строки, но это может изменить смысл данных.
Как удалить дубликаты, не используя array_unique, с помощью array_flip?
array_flip() меняет местами ключи и значения. Если в исходном массиве есть повторяющиеся значения, при перевороте останется только последний ключ (или первое вхождение в зависимости от версии PHP). Затем array_keys() возвращает уникальные значения:
$arr = ['apple', 'banana', 'apple', 'cherry'];
$unique = array_keys(array_flip($arr));
// Результат: [0=>'apple', 1=>'banana', 2=>'cherry']
Проблема: значения должны быть валидными ключами (int или string). Для других типов возникает ошибка. Кроме того, порядок может измениться – в новых версиях PHP порядок сохраняется, но полагаться на это не стоит.
Как получить уникальные значения с сохранением исходного порядка вручную?
Цикл foreach с проверкой через in_array или ассоциативный массив сохраняет первое вхождение и порядок:
$arr = [3, 1, 2, 1, 3];
$seen = [];
$unique = [];
foreach ($arr as $v) {
if (!isset($seen[$v])) {
$seen[$v] = true;
$unique[] = $v;
}
}
// Результат: [0=>3, 1=>1, 2=>2]
Если использовать in_array вместо isset, производительность падает на больших массивах (O(n²)). Всегда используйте ассоциативный массив для быстрой проверки.
Как применить функциональный стиль с array_reduce?
array_reduce позволяет собрать новый массив, добавляя элемент только если он ещё не встречался:
$arr = [1, 2, 2, 3];
$unique = array_reduce($arr, function ($carry, $item) {
if (!in_array($item, $carry)) {
$carry[] = $item;
}
return $carry;
}, []);
// Результат: [1, 2, 3]
Подходит для функциональных цепочек, но по скорости уступает array_unique.
Та же проблема с производительностью in_array. Альтернатива – передавать через use &$seen, но код усложняется.
Как убрать дубликаты в многомерном массиве?
Стандартные функции не подходят. Используется трюк с сериализацией: каждый элемент массива превращается в строку, применяется array_unique, затем обратное преобразование:
$matrix = [[1,2], [3,4], [1,2], [5,6]];
$unique = array_map('unserialize', array_unique(array_map('serialize', $matrix)));
// Результат: [[1,2], [3,4], [5,6]]
Не работает, если подмассивы содержат ресурсы или объекты, не поддающиеся сериализации. Для таких случаев требуется рекурсивное сравнение.
Как получить уникальные значения с сохранением ключей исходного массива (первые вхождения)?
Комбинация array_unique и array_intersect_key позволяет отфильтровать исходный массив, оставив только элементы с ключами, которые присутствуют в уникальном массиве:
$arr = ['a'=>1, 'b'=>2, 'c'=>1, 'd'=>3];
$uniqueKeys = array_unique($arr);
$result = array_intersect_key($arr, $uniqueKeys);
// Результат: ['a'=>1, 'b'=>2, 'd'=>3]
Как удалить дубликаты, если порядок не важен, используя сортировку?
Сортировка массива и последующий проход с сравнением соседних элементов позволяет избавиться от повторений с минимальным использованием памяти:
$arr = [3, 1, 2, 1, 3];
sort($arr);
$unique = [];
$prev = null;
foreach ($arr as $v) {
if ($v !== $prev) {
$unique[] = $v;
$prev = $v;
}
}
// Результат: [1, 2, 3]
Происходит потеря исходного порядка и ключей. Подходит, когда порядок не критичен.
Расширенные примеры фильтрации дубликатов
Ниже приведены более сложные и неочевидные сценарии использования методов получения уникальных значений.
Пример 1: array_unique с разными флагами сравнения
Флаги влияют на то, какие элементы считаются дубликатами. Продемонстрируем различия:
$arr = ['1', 1, 2, '2', '3'];
echo "SORT_REGULAR: ";
print_r(array_unique($arr, SORT_REGULAR));
echo "SORT_NUMERIC: ";
print_r(array_unique($arr, SORT_NUMERIC));
echo "SORT_STRING: ";
print_r(array_unique($arr, SORT_STRING));
SORT_REGULAR: Array ( [0] => 1 [2] => 2 [4] => 3 ) SORT_NUMERIC: Array ( [0] => 1 [2] => 2 [4] => 3 ) SORT_STRING: Array ( [0] => 1 [1] => 2 [2] => 2 [4] => 3 )
Пояснение: SORT_REGULAR считает '1' и 1 разными? В старых версиях PHP они считались одинаковыми, в новых – могут быть разными из-за строгого сравнения. SORT_NUMERIC приводит к числу, '1' и 1 совпадают. SORT_STRING приводит к строке, '2' и 2 разные.
Пример 2: Сохранение ключей с помощью array_intersect_key
Если исходный массив имеет строковые ключи, полезно сохранить их после удаления дубликатов:
$data = [
'first' => 'apple',
'second' => 'banana',
'third' => 'apple',
'fourth' => 'cherry'
];
$unique = array_intersect_key($data, array_unique($data));
print_r($unique);
Array ( [first] => apple [second] => banana [fourth] => cherry )
Пример 3: Удаление дубликатов объектов по свойству
Если массив состоит из объектов, стандартные функции не подходят. Используем цикл с проверкой свойства:
class Item {
public $id;
public $name;
public function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
}
$items = [
new Item(1, 'A'),
new Item(2, 'B'),
new Item(1, 'A'),
new Item(3, 'C')
];
$seen = [];
$uniqueItems = [];
foreach ($items as $item) {
if (!isset($seen[$item->id])) {
$seen[$item->id] = true;
$uniqueItems[] = $item;
}
}
echo count($uniqueItems); // 3
Пример 4: Рекурсивное удаление дубликатов из многомерного массива без сериализации
Для массивов, содержащих вложенные массивы, сериализация может быть ненадёжной. Напишем рекурсивную функцию сравнения:
function recursiveUnique($array) {
$result = [];
foreach ($array as $element) {
if (is_array($element)) {
$element = recursiveUnique($element);
}
if (!in_array($element, $result, true)) {
$result[] = $element;
}
}
return $result;
}
$nested = [[1,2], [3,[4,5]], [1,2], [3,[4,5]]];
$uniqueNested = recursiveUnique($nested);
print_r($uniqueNested);
Array ( [0] => Array ( [0] => 1 [1] => 2 ) [1] => Array ( [0] => 3 [1] => Array ( [0] => 4 [1] => 5 ) ) )
Пояснение: функция обрабатывает вложенные массивы рекурсивно, используя строгое сравнение (===). Для больших массивов может быть медленной.
Пример 5: Комбинирование array_unique и array_values для переиндексации
После array_unique ключи остаются разрозненными. Чтобы получить последовательные числовые ключи, применяется array_values:
$arr = ['x'=>1, 'y'=>2, 'z'=>1];
$unique = array_values(array_unique($arr));
print_r($unique);
Array ( [0] => 1 [1] => 2 )
Пример 6: Использование array_filter со статической переменной
Можно применить array_filter с анонимной функцией, которая сохраняет увиденные значения через статическую переменную:
$arr = [1, 2, 2, 3, 1];
$unique = array_filter($arr, function($v) {
static $seen = [];
if (isset($seen[$v])) return false;
$seen[$v] = true;
return true;
});
print_r(array_values($unique));
Array ( [0] => 1 [1] => 2 [2] => 3 )
Пояснение: статическая переменная сохраняет состояние между вызовами callback. Удобно, если нужно получить уникальные значения без создания внешнего массива.