Як видалити повторювані значення з багатовимірного масиву в 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
Zack Morris

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 вересня 2005 12:09

Ще один Array_Unique для багатодеменсованих масивів. Я тестував це лише на дводементованих масивах, але це, ймовірно, може бути узагальнено для більше, або використане для використання рекурсії.

Ця функція використовує функції serialize, array_unique та unserialization для виконання роботи.


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 Морріс

Навіщо ви додали це? Це так само , як ця відповідь, який в протягом року старше , ніж у вас: stackoverflow.com/a/18373723/870729
random_user_name

3

якщо вам потрібно усунути дублікати на певних клавішах, таких як mysqli id, ось простий функціонал

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

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


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

якщо у вас такий масив:

(користувачі - це ім'я масиву)

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));
клавіатураMmasher

@keyboardSmasher дякую за ваш внесок. Я вніс зміни і зараз це працює. :)
Ануй

1

Багато людей запитували мене, як зробити унікальний багатовимірний масив. Я взяв посилання на ваш коментар, і це мені допомагає.

Перш за все, завдяки @jeromegamez @daveilers за ваше рішення. Але щоразу, коли я давав відповідь, вони запитували мене, як це працює «серіалізувати» та «несеріалізувати». Ось чому я хочу поділитися з вами причиною цього, щоб вона допомогла більшості людей зрозуміти концепцію, що стоїть за цим.

Я пояснюю, чому ми використовуємо "серіалізувати" та "несеріалізувати" кроками:

Крок 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, але якщо вам потрібно скористатися циклом, ви можете ввести $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.