Как удалить повторяющиеся значения из многомерного массива в PHP

Как удалить повторяющиеся значения из многомерного массива в 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
    )

)

person Ian    schedule 21.11.2008    source источник


Ответы (17)


Вот еще один способ. Промежуточные переменные не сохраняются.

Мы использовали это, чтобы исключить дублирование результатов из множества перекрывающихся запросов.

$input = array_map("unserialize", array_unique(array_map("serialize", $input)));
person daveilers    schedule 03.06.2009
comment
Из-за десериализации это медленнее и медленнее, чем больше и сложнее массив. Есть причина, по которой я использовал array_intersect_key (за полгода до этого ответа). - person OIS; 09.02.2013
comment
@OIS, я думаю, они хотели один лайнер. нужно было просто переписать как $ no-duplicates = array_intersect_key ($ array, array_unique (array_map ('serialize'), $ array)); - person trevorkavanaugh; 04.06.2013
comment
@OIS хорошо протестировал его, была опечатка, но он работает ... спасибо, чувак !: $ no_duplicates = array_intersect_key ($ array, array_unique (array_map ('serialize', $ array))); - person trevorkavanaugh; 04.06.2013
comment
если вы хотите, чтобы индекс был непрерывным, используйте array_values, т.е. $ input = array_values ​​(array_map (unserialize, array_unique (array_map (serialize, $ input)))); - person lbsweek; 17.04.2014
comment
В настоящее время вы, вероятно, выберете json_encode и json_decode вместо сериализации PHP. должен иметь преимущества для предоставленных значений и, вы не сталкиваетесь с деталями сериализации PHP, которые сериализуют / десериализовывают поставляются с и, скорее всего, являются нежелательными. - person hakre; 23.08.2014
comment
$ input = array_values ​​($ input); Чтобы исправить отсутствующие ключи массива внутри массива - person Dhawal Naik; 28.08.2015
comment
Хотя это круто, я не хочу зависеть от serialize и unserialize. То, как это реализовано в PHP, несколько раз менялось за кулисами. Дольше всего ссылок не хранилось. Сейчас они есть, но только на ссылки в сериализованном массиве. (Я знаю, что в вопросе не было предметов). Лучше было бы array_unique($input, SORT_REGULAR) или какая-нибудь пользовательская функция. - person Michael; 23.01.2016
comment
Помните, что serialize(array('a' => '1', 'b' => '1')) отличается от serialize(array('b' => '1', 'a' => '1')). Эта опция не работает для массивов, используемых как sets или (hash)maps. - person Andras Gyomrey; 19.10.2016
comment
Еще лучше с одиночными запятыми! $input = array_map('unserialize', array_unique(array_map('serialize', $input))); - person Heitor; 23.08.2017
comment
@Michael - Из текущего документа PHP: обратите внимание, что array_unique () не предназначен для работы с многомерными массивами. - person gmeben; 08.03.2018
comment
Хороший ответ, но он не поддерживает записи с другим порядком, например [['foo', 'bar'], ['bar', 'foo']]. Чтобы использовать ваш фрагмент, я отсортировал дочерние массивы. - person AFA Med; 19.06.2018

Начиная с 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() не предназначен для работы с многомерными массивами.

person Ja͢ck    schedule 22.08.2013
comment
Думаю, это более быстрое и понятное решение, чем принятое! давайте проголосуем за это! :) Хммм на сайте php мы видим, что это не так быстро, как я думал ... - person Andron; 05.05.2015
comment
Странно, что использование флага SORT_REGULAR просто не работает для меня, чтобы удалить повторяющиеся массивы. - person Stefan; 30.07.2015
comment
@Stefan, тогда ты можешь задать новый вопрос. - person Ja͢ck; 30.07.2015
comment
На этот вопрос уже есть другие рабочие ответы, которые я проверил (см., Например, принятый ответ). Я просто не могу проверить этот конкретный ответ для моего конкретного примера. - person Stefan; 30.07.2015
comment
@Stefan, не могли бы вы предоставить pastebin для решения вашей конкретной проблемы? Просто сказать мне, что это не работает, не поможет мне понять. - person Ja͢ck; 30.07.2015
comment
@Stefan Ты прав; похоже, что он не дает правильных результатов, но, вероятно, это ошибка, потому что работает с PHP 7 = / - person Ja͢ck; 30.07.2015
comment
Очень интересно ... Большое спасибо, Джек, за внимание к этому - потрясающий ответ. В любом случае, поскольку я не могу изменить версию php на сервере, мне просто нужно будет использовать одно из других решений. - person Stefan; 30.07.2015
comment
В моем случае это тоже работает, но кого-нибудь еще беспокоит эта заметка в документе array_unique ()? php.net/manual/en/ - person Arleigh Hix; 02.05.2016
comment
@Jack Вы правы, это ошибка PHP 5.6.23: eval.in/645675, но исправлено с PHP 7.0.8: eval.in/645676 - person Zack Morris; 21.09.2016
comment
Остерегайтесь array(1, 2) отличается от array(2, 1). Это не сработает для массивов, используемых как sets. - person Andras Gyomrey; 19.10.2016
comment
@AndrasGyomrey, если вы хотите выразить множества с помощью массивов, вы должны нормализовать их. - person Ja͢ck; 19.10.2016
comment
Либо вы используете in_array, либо убедитесь, что ваши элементы можно использовать как ключи массива (сериализуемые), и вы используете какое-то фиктивное значение массива. Это зависит от вашей реализации. Для первой реализации вы не можете использовать этот подход. - person Andras Gyomrey; 20.10.2016
comment
Это правильное решение по сравнению с сериализацией / десериализацией ответа. Обратите внимание, что уникальный массив выполняется довольно быстро по сравнению с сериализацией / десериализацией. Это может быть проблемой, если вы делаете сотни или тысячи массивов уникальными. - person PeterM; 12.10.2017
comment
Обратите внимание, что это вызывает странное поведение с ассоциированными массивами, которые имеют логические истинные значения (они исчезают). Пример: 3v4l.org/Yj6Y1 (show_in_configurator исчез). - person Ken; 10.03.2021

У меня была аналогичная проблема, но я нашел для нее 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'));

?>
person Rajendrasinh    schedule 09.05.2012
comment
Это отвечает на другой вопрос. См. Здесь: stackoverflow .com / questions / 4585208 / - person OIS; 09.02.2013
comment
Отличная функция! и если вы имеете дело с объектами: if (! isset ($ array - ›$ v -› $ key)) $ array [$ v - ›$ key] = & $ v; - person Playnox; 17.03.2016

Другой путь. Также сохранит ключи.

function array_unique_multidimensional($input)
{
    $serialized = array_map('serialize', $input);
    $unique = array_unique($serialized);
    return array_intersect_key($input, $unique);
}
person OIS    schedule 21.11.2008
comment
Для больших массивов этот метод часто как минимум на 50% быстрее принятого ответа. - person Lorien Brune; 13.05.2020

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);

Это удалит повторяющиеся имена из массива. уникальный по ключу

person Mahak Choudhary    schedule 23.02.2018
comment
Убедитесь, что ключи $array начинаются с 0. Возможно, ключи $array начинаются с другого номера, если $array является результатом предыдущей манипуляции с массивом. Используйте array_values, чтобы сбросить ключи обратно на 0 - person stevevance; 28.05.2020

Если «удалить дубликаты» означает «удалить дубликаты, но оставить один там», решением может быть сначала применить 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
        )

)
person automatix    schedule 20.05.2016

Комментарии пользователей к документации array_unique () содержат множество решений этой проблемы. Вот один из них:

kenrbnsn at rbnsn dot com
27 сентября 2005 г., 12:09

Еще один Array_Unique для многомерных массивов. Я тестировал это только на двумерных массивах, но, вероятно, его можно было бы обобщить для большего или использовать рекурсию.

Эта функция использует функции serialize, array_unique и unserialize для выполнения своей работы.


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.

person Paige Ruten    schedule 21.11.2008

если вам нужно удалить дубликаты на определенных ключах, таких как идентификатор mysqli, вот простая функция

function search_array_compact($data,$key){
    $compact = [];
    foreach($data as $row){
        if(!in_array($row[$key],$compact)){
            $compact[] = $row;
        }
    }
    return $compact;
}

Бонусные баллы. Вы можете передать массив ключей и добавить внешний foreach, но каждый дополнительный ключ будет в 2 раза медленнее.

person r3wt    schedule 08.11.2014

Очень простой и логичный способ Уникального многомерного массива заключается в следующем:

Если у вас есть такой массив:

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
        )
)

Надеюсь, это все прояснит.

person Anand agrawal    schedule 19.01.2018

если у вас есть такой массив:

(пользователи - это имя массива)

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

person Limon    schedule 07.08.2015

Многие спрашивали меня, как сделать уникальный многомерный массив. Я взял ссылку из вашего комментария, и это мне помогает.

Прежде всего, спасибо @jeromegamez @daveilers за ваше решение. Но каждый раз, когда я отвечал, меня спрашивали, как работают эти «сериализации» и «десериализации». Вот почему я хочу поделиться с вами причиной этого, чтобы это помогло большему количеству людей понять концепцию, лежащую в основе этого.

Я объясняю, почему мы используем «сериализацию» и «десериализацию» поэтапно:

Шаг 1. Преобразуйте многомерный массив в одномерный массив

Чтобы преобразовать многомерный массив в одномерный массив, сначала сгенерируйте представление потока байтов всех элементов (включая вложенные массивы) внутри массива. Функция serialize () может генерировать представление значения в виде потока байтов. Чтобы сгенерировать представление потока байтов для всех элементов, вызовите функцию serialize () внутри функции array_map () как функцию обратного вызова. Результатом будет одномерный массив независимо от того, сколько уровней имеет многомерный массив.

Шаг 2. Сделайте значения уникальными

Чтобы сделать этот одномерный массив уникальным, используйте функцию array_unique ().

Шаг 3. Преобразуйте его в многомерный массив

Хотя теперь массив уникален, значения выглядят как представление байтового потока. Чтобы вернуть его обратно в многомерный массив, используйте функцию unserialize ().

$input = array_map("unserialize", array_unique(array_map("serialize", $input)));

Еще раз спасибо за все это.

person Manish    schedule 01.09.2016

Легко читаемое решение, возможно, не самое эффективное:

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;

} 
person pixeline    schedule 19.06.2009

Как говорят люди, 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

person Anuj    schedule 18.04.2015
comment
Анудж, не могли бы вы отредактировать свой ответ? Есть ошибка. Он должен закончиться $output = array_map('unserialize', array_keys($result)); - person keyboardSmasher; 16.05.2015
comment
@keyboardSmasher благодарим вас за ваш вклад. Я внес изменения, и теперь все работает. :) - person Anuj; 22.05.2015

Я пробовал это, чтобы удалить дубликаты.

$array = array_map("unserialize", array_unique(array_map("serialize", $array)));
person Mohit Sidoliya    schedule 09.10.2020

Если у вас есть такой массив

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
)
)

or

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;
person milic    schedule 29.11.2014

Я много думал над этой проблемой и решил, что оптимальное решение должно соответствовать двум правилам.

  1. Для масштабируемости измените массив на месте; без копирования в новый массив
  2. Для производительности каждое сравнение нужно производить только один раз.

Имея это в виду и учитывая все причуды 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); после указанного выше, чтобы перенумеровать ключи.

person Snake    schedule 05.03.2015

На основании ответа, помеченного как правильный, добавляю свой ответ. Небольшой код добавлен только для сброса индексов -

$input = array_values(array_map("unserialize", array_unique(array_map("serialize", $inputArray))));
person Gagan    schedule 14.02.2020