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


306

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

)

Ответы:


637

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

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

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

23
Из-за несериализации это происходит все медленнее и медленнее, чем больше и сложнее массив. Есть причина, по которой я использовал array_intersect_key (за полгода до ответа).
OIS

11
@OIS только что проверил, имел опечатку, но она работает .. спасибо, чувак !: $ no_duplicates = array_intersect_key ($ array, array_unique (array_map ('serialize', $ array)));
trevorkavanaugh

3
если вы хотите, чтобы индекс был непрерывным, используйте array_values, т.е. $ input = array_values ​​(array_map ("unserialize", array_unique (array_map ("serialize", $ input))));
lbsweek

4
В настоящее время вы, вероятно, выбрали бы json_encode и json_decode вместо сериализации PHP. должны иметь преимущества для предоставленных значений, и вы не столкнетесь с деталями сериализации PHP, которые сериализуют / десериализуют корабли и, скорее всего, являются нежелательными.
Хакре

2
Осторожно, serialize(array('a' => '1', 'b' => '1'))это отличается от serialize(array('b' => '1', 'a' => '1')). Эта опция потерпит неудачу для массивов, используемых как setsили (hash)maps.
Андрас Джомрей

240

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


2
Думаю, это более быстрое и понятное решение, чем принятое! давайте проголосуем за это! :) Хм, на сайте php мы видим, что это не так быстро, как я думал ...
Андрон

4
Странно, что использование флага SORT_REGULAR просто не работает для меня, чтобы удалить дублирующиеся массивы.
Стефан

4
@Stefan Ты прав; похоже, что он не дает правильных результатов, но это, вероятно, ошибка, потому что он работает с PHP 7 = /
Ja͢ck

4
Это также работает в моем случае, но кого еще беспокоит эта заметка в документе array_unique ()? php.net/manual/en/…
Арли Хикс

2
@Jack Вы правы, это ошибка в PHP 5.6.23: eval.in/645675, но исправлена ​​в PHP 7.0.8: eval.in/645676
Зак Моррис

63

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

?>

1
Это отвечает на другой вопрос. Смотрите здесь: stackoverflow.com/questions/4585208/…
OIS

Отличная функция! и если вы имеете дело с объектами: if (! isset ($ array -> $ v -> $ key)) $ array [$ v -> $ key] = & $ v;
Playnox

35

По-другому. Сохранит и ключи.

function array_unique_multidimensional($input)
{
    $serialized = array_map('serialize', $input);
    $unique = array_unique($serialized);
    return array_intersect_key($input, $unique);
}

Для больших массивов этот метод часто по крайней мере на 50% быстрее, чем принятый ответ.
Лориен Брюн

21

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

kenrbnsn at rbnsn dot com
27-Sep-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 .


18

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

)

18
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»
stevevance


4

Просто используйте параметр SORT_REGULAR в качестве второго параметра.

$uniqueArray = array_unique($array, SORT_REGULAR);

1
SORT_REGULAR работает только в PHP 7, потому что в PHP 5 есть ошибка (хотя @ r3wt является правильным в соответствии с документацией), см. Мой комментарий в ответе для работающего примера stackoverflow.com/questions/307674/…
Zack Morris

Зачем ты это добавил? Это то же самое, что и этот ответ, который старше вашего на год: stackoverflow.com/a/18373723/870729
random_user_name

3

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

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

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


3

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

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

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

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


2

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

(users - имя массива)

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


1

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

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;

} 

1

Как говорят люди, 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 спасибо за ваш вклад. Я сделал изменения, и теперь это работает. :)
Anuj

1

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

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

Я объясняю, почему мы используем 'serialize' и 'unserialize' по шагам:

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

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

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

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

Шаг 3: Верните его в многомерный массив

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

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

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


0

Альтернатива сериализации и уникальность

$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"
        }
}

* /


0

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

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;

0

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

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


0

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

$input = array_values(array_map("unserialize", array_unique(array_map("serialize", $inputArray))));
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.