Как удалить повторяющиеся значения из многомерного массива в PHP?
Пример массива:
Array
(
[0] => Array
(
[0] => abc
[1] => def
)
[1] => Array
(
[0] => ghi
[1] => jkl
)
[2] => Array
(
[0] => mno
[1] => pql
)
[3] => Array
(
[0] => abc
[1] => def
)
[4] => Array
(
[0] => ghi
[1] => jkl
)
[5] => Array
(
[0] => mno
[1] => pql
)
)






Комментарии пользователей к документации array_unique () предлагают множество решений для этого. Вот один из них:
kenrbnsn at rbnsn dot com
27-Sep-2005 12:09Yet another Array_Unique for multi-demensioned arrays. I've only tested this on two-demensioned arrays, but it could probably be generalized for more, or made to use recursion.
This function uses the serialize, array_unique, and unserialize functions to do the work.
function multi_unique($array) { foreach ($array as $k=>$na) $new[$k] = serialize($na); $uniq = array_unique($new); foreach($uniq as $k=>$ser) $new1[$k] = unserialize($ser); return ($new1); }
Это от http://ca3.php.net/manual/en/function.array-unique.php#57202.
Другой путь. Также сохранит ключи.
function array_unique_multidimensional($input)
{
$serialized = array_map('serialize', $input);
$unique = array_unique($serialized);
return array_intersect_key($input, $unique);
}
Вот еще один способ. Промежуточные переменные не сохраняются.
Мы использовали это, чтобы исключить дублирование результатов из множества перекрывающихся запросов.
$input = array_map("unserialize", array_unique(array_map("serialize", $input)));
Из-за десериализации это медленнее и медленнее, чем больше и сложнее массив. Есть причина, по которой я использовал array_intersect_key (за полгода до этого ответа).
@OIS, я думаю, они хотели один лайнер. нужно было просто переписать как $ no-duplicates = array_intersect_key ($ array, array_unique (array_map ('serialize'), $ array));
@OIS хорошо протестировал его, была опечатка, но он работает ... спасибо, чувак !: $ no_duplicates = array_intersect_key ($ array, array_unique (array_map ('serialize', $ array)));
если вы хотите, чтобы индекс был непрерывным, используйте array_values, т.е. $ input = array_values (array_map ("unserialize", array_unique (array_map ("serialize", $ input))));
В настоящее время вы, вероятно, выберете json_encode и json_decode вместо сериализации PHP. должен иметь преимущества для значений, предоставленных и, которые вы не сталкиваетесь с деталями сериализации PHP, которые сериализуются / десериализованы, поставляются с и, скорее всего, являются нежелательными.
$ input = array_values ($ input); Чтобы исправить отсутствующие ключи массива внутри массива
Хотя это круто, я не хочу зависеть от serialize и unserialize. То, как это реализовано в PHP, несколько раз менялось за кулисами. Дольше всего ссылок не хранилось. Сейчас они есть, но только на ссылки в сериализованном массиве. (Я знаю, что в вопросе не было объектов). Лучше всего будет array_unique($input, SORT_REGULAR) или какая-нибудь пользовательская функция.
Помните, что serialize(array('a' => '1', 'b' => '1')) отличается от serialize(array('b' => '1', 'a' => '1')). Эта опция не работает для массивов, используемых как sets или (hash)maps.
Еще лучше с одиночными запятыми! $input = array_map('unserialize', array_unique(array_map('serialize', $input)));
@Michael - Из текущего документа PHP: «Обратите внимание, что array_unique () не предназначен для работы с многомерными массивами».
Хороший ответ, но он не поддерживает записи с другим порядком, например [['foo', 'bar'], ['bar', 'foo']]. Чтобы использовать ваш фрагмент, я отсортировал дочерние массивы.
Легко читаемое решение, возможно, не самое эффективное:
function arrayUnique($myArray){
if (!is_array($myArray))
return $myArray;
foreach ($myArray as &$myvalue){
$myvalue=serialize($myvalue);
}
$myArray=array_unique($myArray);
foreach ($myArray as &$myvalue){
$myvalue=unserialize($myvalue);
}
return $myArray;
}
У меня была аналогичная проблема, но я нашел для нее 100% рабочее решение.
<?php
function super_unique($array,$key)
{
$temp_array = [];
foreach ($array as &$v) {
if (!isset($temp_array[$v[$key]]))
$temp_array[$v[$key]] =& $v;
}
$array = array_values($temp_array);
return $array;
}
$arr = "";
$arr[0]['id']=0;
$arr[0]['titel'] = "ABC";
$arr[1]['id']=1;
$arr[1]['titel'] = "DEF";
$arr[2]['id']=2;
$arr[2]['titel'] = "ABC";
$arr[3]['id']=3;
$arr[3]['titel'] = "XYZ";
echo "<pre>";
print_r($arr);
echo "unique*********************<br/>";
print_r(super_unique($arr,'titel'));
?>
Это отвечает на другой вопрос. Смотрите здесь: stackoverflow.com/questions/4585208/…
Отличная функция! и если вы имеете дело с объектами: if (! isset ($ array -> $ v -> $ key)) $ array [$ v -> $ key] = & $ v;
Начиная с 5.2.9 вы можете использовать array_unique(), если вы используете флаг SORT_REGULAR следующим образом:
array_unique($array, SORT_REGULAR);
Это заставляет функцию сравнивать элементы на равенство, как если бы использовался $a == $b, что идеально подходит для вашего случая.
Выход
Array
(
[0] => Array
(
[0] => abc
[1] => def
)
[1] => Array
(
[0] => ghi
[1] => jkl
)
[2] => Array
(
[0] => mno
[1] => pql
)
)
Однако имейте в виду, что документация утверждает:
array_unique()is not intended to work on multi dimensional arrays.
Думаю, это более быстрое и понятное решение, чем принятое! давайте проголосуем за это! :) Хммм на сайте php мы видим, что это не так быстро, как я думал ...
Странно, что использование флага SORT_REGULAR просто не работает для меня, чтобы удалить повторяющиеся массивы.
@Stefan, тогда ты можешь задать новый вопрос.
На этот вопрос уже есть другие рабочие ответы, которые я проверил (см., Например, принятый ответ). Я просто не могу проверить этот конкретный ответ для моего конкретного примера.
@Stefan, не могли бы вы предоставить pastebin для решения вашей конкретной проблемы? Просто сказать мне, что это не работает, не поможет мне понять.
@Stefan Ты прав; похоже, что он не дает правильных результатов, но, вероятно, это ошибка, потому что работает с PHP 7 = /
Очень интересно ... Большое спасибо, Джек, за внимание к этому - потрясающий ответ. В любом случае, поскольку я не могу изменить версию php на сервере, мне просто нужно будет использовать одно из других решений.
В моем случае это тоже работает, но кого-нибудь еще беспокоит эта заметка в документе array_unique ()? php.net/manual/en/…
@Jack Вы правы, это ошибка в PHP 5.6.23: eval.in/645675, но исправлена в PHP 7.0.8: eval.in/645676
Будьте осторожны, array(1, 2) отличается от array(2, 1). Это не сработает для массивов, используемых как sets.
@AndrasGyomrey, если вы хотите выразить множества с помощью массивов, вы должны нормализовать их.
Либо вы используете in_array, либо убедитесь, что ваши элементы можно использовать как ключи массива (сериализуемые), и вы используете какое-то фиктивное значение массива. Это зависит от вашей реализации. Для первой реализации вы не можете использовать этот подход.
Это правильное решение по сравнению с сериализацией / десериализацией ответа. Обратите внимание, что уникальный массив выполняется довольно быстро по сравнению с сериализацией / десериализацией. Этот мощь будет проблемой, если вы сделаете сотни / тысячи уникальных массивов.
Обратите внимание, что это вызывает странное поведение с ассоциированными массивами, которые имеют логические истинные значения (они исчезают). Пример: 3v4l.org/Yj6Y1 (пропал show_in_configurator).
Альтернатива сериализации и уникальности
$test = [
['abc','def'],
['ghi','jkl'],
['mno','pql'],
['abc','def'],
['ghi','jkl'],
['mno','pql'],
];
$result = array_reduce(
$test,
function($carry,$item){
if (!in_array($item,$carry)) {
array_push($carry,$item);
}
return $carry;
},
[]
);
var_dump($result);
/*
php unique.php
array(3) {
[0] =>
array(2) {
[0] =>
string(3) "abc"
[1] =>
string(3) "def"
}
[1] =>
array(2) {
[0] =>
string(3) "ghi"
[1] =>
string(3) "jkl"
}
[2] =>
array(2) {
[0] =>
string(3) "mno"
[1] =>
string(3) "pql"
}
}
* /
если вам нужно удалить дубликаты на определенных ключах, таких как идентификатор mysqli, вот простая функция
function search_array_compact($data,$key){
$compact = [];
foreach($data as $row){
if (!in_array($row[$key],$compact)){
$compact[] = $row;
}
}
return $compact;
}
Бонусные очки Вы можете передать массив ключей и добавить внешний foreach, но каждый дополнительный ключ будет в 2 раза медленнее.
Если у вас есть такой массив
data = array
(
[0] => array
(
[subject] => a
[object] => c
),
[1] => array
(
[subject] => b
[object] => d
),
[2] => array
(
[subject] => d
[object] => b
),
[3] => array
(
[subject] => d
[object] => c
),
[4] => array
(
[subject] => c
[object] => a
),
[5] => array
(
[subject] => c
[object] => d
)
)
и вы хотите получить такие массивы:
data = array
(
[0] => array
(
[subject] => a
[object] => c
),
[1] => array
(
[subject] => b
[object] => d
),
[2] => array
(
[subject] => d
[object] => c
)
)
или же
data = array
(
[0] => array
(
[subject] => d
[object] => b
),
[1] => array
(
[subject] => c
[object] => a
),
[2] => array
(
[subject] => c
[object] => d
)
)
следующий код может помочь
$data1 = array();
$data1 = $data;
for($q=0;$q<count($data);$q++)
{
for($p=0;$p<count($data1);$p++)
{
if (($data[$q]["subject"] == $data1[$p]["object"]) && ($data[$q]["object"] == $data1[$p]["subject"]))
{
$data1[$p]["subject"] = $data[$q]["subject"];
$data1[$p]["object"] = $data[$q]["object"];
}
}
}
$data1 = array_values(array_map("unserialize", array_unique(array_map("serialize", $data1))));
$data = $data1;
Я много думал над этой проблемой и решил, что оптимальное решение должно соответствовать двум правилам.
Имея это в виду и учитывая все причуды PHP, ниже представлено решение, которое я придумал. В отличие от некоторых других ответов, он может удалять элементы на основе любого ключа (ов), который вы хотите. Ожидается, что входной массив будет числовыми клавишами.
$count_array = count($input);
for ($i = 0; $i < $count_array; $i++) {
if (isset($input[$i])) {
for ($j = $i+1; $j < $count_array; $j++) {
if (isset($input[$j])) {
//this is where you do your comparison for dupes
if ($input[$i]['checksum'] == $input[$j]['checksum']) {
unset($input[$j]);
}
}
}
}
}
Единственный недостаток состоит в том, что ключи не в порядке после завершения итерации. Это не проблема, если впоследствии вы будете использовать только циклы foreach, но если вам нужно использовать цикл for, вы можете поместить $input = array_values($input); после вышеуказанного, чтобы перенумеровать ключи.
Просто используйте опцию SORT_REGULAR в качестве второго параметра.
$uniqueArray = array_unique($array, SORT_REGULAR);
SORT_REGULAR работает только в PHP 7, потому что в PHP 5 есть ошибка (хотя @ r3wt верен согласно документации), см. Мой комментарий в ответе на запускаемый пример stackoverflow.com/questions/307674/…
Зачем ты это добавляешь? Это то же самое, что и этот ответ, который старше вашего более чем на год: stackoverflow.com/a/18373723/870729
Поскольку люди говорят, что array_unique() очень медленный, вот фрагмент, который я использую для одноуровневого многомерного массива.
$serialized_array = array_map("serialize", $input);
foreach ($serialized_array as $key => $val) {
$result[$val] = true;
}
$output = array_map("unserialize", (array_keys($result)));
Ссылка на примечание первого пользователя о array_unique()страница функций в php.net
Анудж, не могли бы вы отредактировать свой ответ? Есть ошибка. Должен закончиться $output = array_map('unserialize', array_keys($result));
@keyboardSmasher благодарим вас за ваш вклад. Я внес изменения, и теперь все работает. :)
если у вас есть такой массив:
(пользователи - это имя массива)
Array=>
[0] => (array)
'user' => 'john'
'age' => '23'
[1] => (array)
'user' => 'jane'
'age' => '20'
[2]=> (array)
'user' => 'john'
'age' => '23'
и вы хотите удалить дубликаты ... тогда:
$serialized = array();
for ($i=0; $i < sizeof($users); $i++) {
$test = in_array($users['user'], $serialized);
if ($test == false) {
$serialized[] = $users['user'];
}
}
может быть решением: P
Если «удалить дубликаты» означает «удалить дубликаты, но оставить один там», решением может быть сначала применить array_unique(...) к «столбцу идентификатора», а затем удалить из исходного массива все ключи, которые были удалены из столбца. множество:
$array = [
[
'id' => '123',
'foo' => 'aaa',
'bar' => 'bbb'
],
[
'id' => '123',
'foo' => 'ccc',
'bar' => 'ddd'
],
[
'id' => '567',
'foo' => 'eee',
'bar' => 'fff'
]
];
$ids = array_column($array, 'id');
$ids = array_unique($ids);
$array = array_filter($array, function ($key, $value) use ($ids) {
return in_array($value, array_keys($ids));
}, ARRAY_FILTER_USE_BOTH);
Результат:
Array
(
[0] => Array
(
[id] => 123
[foo] => aaa
[bar] => bbb
)
[2] => Array
(
[id] => 567
[foo] => eee
[bar] => fff
)
)
Многие спрашивали меня, как сделать уникальный многомерный массив. Я взял ссылку из вашего комментария, и это мне помогает.
Прежде всего, спасибо @jeromegamez @daveilers за ваше решение. Но каждый раз, когда я отвечал, меня спрашивали, как работают эти «сериализация» и «десериализация». Вот почему я хочу поделиться с вами причиной этого, чтобы это помогло большему количеству людей понять концепцию, лежащую в основе этого.
Я объясняю, почему мы используем «сериализацию» и «десериализацию» поэтапно:
Шаг 1. Преобразуйте многомерный массив в одномерный массив
Чтобы преобразовать многомерный массив в одномерный массив, сначала сгенерируйте представление потока байтов всех элементов (включая вложенные массивы) внутри массива. Функция serialize () может генерировать представление значения в виде потока байтов. Чтобы сгенерировать представление потока байтов для всех элементов, вызовите функцию serialize () внутри функции array_map () как функцию обратного вызова. Результатом будет одномерный массив независимо от того, сколько уровней имеет многомерный массив.
Шаг 2. Сделайте ценности уникальными
Чтобы сделать этот одномерный массив уникальным, используйте функцию array_unique ().
Шаг 3: преобразовать его в многомерный массив
Хотя теперь массив уникален, значения выглядят как представление байтового потока. Чтобы вернуть его обратно в многомерный массив, используйте функцию unserialize ().
$input = array_map("unserialize", array_unique(array_map("serialize", $input)));
Еще раз спасибо за все это.
Очень простой и логичный способ Уникального многомерного массива заключается в следующем:
Если у вас есть такой массив:
Array
(
[Key1] => Array
(
[0] => Value1
[1] => Value2
[2] => Value1
[3] => Value3
[4] => Value1
)
[Key2] => Array
(
[0] => Value1
[1] => Value2
[2] => Value1
[3] => Value3
[4] => Value4
)
)
используйте foreach, чтобы решить эту проблему:
foreach($array as $k=>$v){
$unique=array_unique($v);
$array[$k]=$unique;
}
это даст вам следующий результат:
Array
(
[Key1] => Array
(
[0] => Value1
[1] => Value2
[3] => Value3
)
[Key2] => Array
(
[0] => Value1
[1] => Value2
[3] => Value3
[4] => Value4
)
)
и если вы хотите изменить порядок клавиш,
foreach($array as $k=>$v){
$unique= array_values(array_unique($v));
$array[$k]=$unique;
}
Эта операция даст вам такие упорядоченные ключевые значения:
Array
(
[Key1] => Array
(
[0] => Value1
[1] => Value2
[2] => Value3
)
[Key2] => Array
(
[0] => Value1
[1] => Value2
[2] => Value3
[3] => Value4
)
)
Надеюсь, это все прояснит.
Array
(
[0] => Array
(
[id] => 1
[name] => john
)
[1] => Array
(
[id] => 2
[name] => smith
)
[2] => Array
(
[id] => 3
[name] => john
)
[3] => Array
(
[id] => 4
[name] => robert
)
)
$temp = array_unique(array_column($array, 'name'));
$unique_arr = array_intersect_key($array, $temp);
Это удалит повторяющиеся имена из массива. уникальный по ключу
Убедитесь, что ключи $array начинаются с «0». Возможно, ключи $array начинаются с другого номера, если $array является результатом предшествующей манипуляции с массивом. Используйте array_values, чтобы сбросить ключи обратно на "0"
Простое решение:
array_unique($array, SORT_REGULAR)
На основании ответа, помеченного как правильный, добавляю свой ответ. Небольшой код добавлен только для сброса индексов -
$input = array_values(array_map("unserialize", array_unique(array_map("serialize", $inputArray))));
Я пробовал это, чтобы удалить дубликаты.
$array = array_map("unserialize", array_unique(array_map("serialize", $array)));
Для больших массивов этот метод часто как минимум на 50% быстрее принятого ответа.