Como remover valores duplicados de uma matriz multidimensional em PHP


306

Como posso remover valores duplicados de uma matriz multidimensional no PHP?

Matriz de exemplo:

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
    )

)

Respostas:


637

Aqui está outro caminho. Nenhuma variável intermediária é salva.

Usamos isso para desduplicar os resultados de uma variedade de consultas sobrepostas.

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

23
Por causa da desserialização, isso é cada vez mais lento, quanto maior e mais complexo o conjunto. Há uma razão pela qual usei array_intersect_key (meio ano antes desta resposta).
OIS

11
@ OIS bem testado, teve um erro de digitação, mas funciona .. obrigado cara !: $ no_duplicates = array_intersect_key ($ array, array_unique (array_map ('serialize', $ array)));
precisa saber é o seguinte

3
se você deseja que o índice seja contínuo, use array_values, ou seja, $ input = array_values ​​(array_map ("unserialize", array_unique (array_map ("serialize", $ input))));
lbsweek

4
Atualmente, você provavelmente optaria por json_encode e json_decode em vez da serialização do PHP. deve ter benefícios para os valores fornecidos e você não encontra detalhes de serialização do PHP que serializam / desserializam os navios e provavelmente não são desejados.
hakre

2
Cuidado com isso serialize(array('a' => '1', 'b' => '1'))é diferente de serialize(array('b' => '1', 'a' => '1')). Esta opção falhará nas matrizes usadas como setsou (hash)maps.
Andras Gyomrey

240

Desde 5.2.9, você pode usar array_unique()se usar o SORT_REGULARsinalizador da seguinte maneira:

array_unique($array, SORT_REGULAR);

Isso faz com que a função compare elementos para igualdade como se $a == $bestivesse sendo usada, o que é perfeito para o seu caso.

Resultado

Array
(
    [0] => Array
        (
            [0] => abc
            [1] => def
        )

    [1] => Array
        (
            [0] => ghi
            [1] => jkl
        )

    [2] => Array
        (
            [0] => mno
            [1] => pql
        )

)

Porém, lembre-se de que a documentação declara:

array_unique() não se destina a trabalhar em matrizes multidimensionais.


2
Eu acho que essa é uma solução mais rápida e clara do que a aceita! vamos votar neste! :) Hmmm no local php podemos ver que não é tão rápido, como eu pensava ...
Andron

4
Estranho que usar o sinalizador SORT_REGULAR simplesmente não funcione para mim, para remover matrizes duplicadas.
Stefan

4
@Stefan Você está certo; parece não dar os resultados corretos, mas provavelmente é um bug, porque funciona com o PHP 7 = /
Ja͢ck

4
Isso também parece funcionar no meu caso, mas alguém mais se incomoda com esta nota no documento array_unique ()? php.net/manual/pt/…
Arleigh Hix

2
@Jack Você está certo este é um bug no PHP 5.6.23: eval.in/645675 mas é fixo a partir do PHP 7.0.8: eval.in/645676
Zack Morris

63

Eu tive um problema semelhante, mas encontrei uma solução 100% funcional para ele.

<?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
Isso responde a uma pergunta diferente. Veja aqui: stackoverflow.com/questions/4585208/…
OIS

Ótima função! e caso você esteja lidando com objetos: if (! isset ($ array -> $ v -> $ key)) $ array [$ v -> $ key] = & $ v;
Playnox 17/03/16

35

Outra maneira. Também preservará as chaves.

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

Para matrizes grandes, esse método geralmente é pelo menos 50% mais rápido que a resposta aceita.
Lorien Brune

21

Os comentários do usuário na documentação do array_unique () têm muitas soluções para isso. Aqui está um deles:

kenrbnsn na rbnsn dot com
27-Set-2005 12:09

Mais um Array_Unique para arrays multi-dimensionados. Eu só testei isso em matrizes com duas dimensões, mas provavelmente poderia ser generalizado por mais, ou feito para usar recursão.

Essa função usa as funções serialize, array_unique e desserialize para fazer o trabalho.


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

Isto é de http://ca3.php.net/manual/en/function.array-unique.php#57202 .


18

Se "remover duplicados" significa "remover duplicados, mas deixe um lá", uma solução pode ser aplicar array_unique(...)primeiro a "coluna identificadora" e depois remover na matriz original todas as chaves que foram removidas da matriz de colunas :

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

O resultado é:

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

Isso removerá os nomes duplicados da matriz. único por chave


Verifique se $arrayas teclas começam em "0". É possível que $arrayas teclas comecem com outro número se $arrayfor o resultado de uma manipulação anterior da matriz. Use array_valuespara redefinir as teclas de volta para "0"
stevevance


4

Basta usar a opção SORT_REGULAR como segundo parâmetro.

$uniqueArray = array_unique($array, SORT_REGULAR);

1
SORT_REGULAR só funciona no PHP 7 porque o PHP 5 tem um bug (embora @ r3wt esteja correto conforme a documentação), veja meu comentário na resposta para um exemplo executável stackoverflow.com/questions/307674/…
Zack Morris

Por que você adicionaria isso? É o mesmo que essa resposta, que é mais de um ano mais velho do que sua: stackoverflow.com/a/18373723/870729
random_user_name

3

se você precisar eliminar duplicatas em chaves específicas, como um ID mysqli, aqui está uma função simples

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

Pontos de bônus Você pode passar uma matriz de chaves e adicionar um foreach externo, mas será 2x mais lento por chave adicional.


3

Uma maneira muito fácil e lógica de criar uma matriz multidimensional exclusiva é a seguinte,

Se você tiver um array como este:

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

use foreachpara resolver isso:

foreach($array as $k=>$v){
    $unique=array_unique($v);
    $array[$k]=$unique;
}

isso lhe dará o seguinte resultado:

Array
(
    [Key1] => Array
        (
            [0] => Value1
            [1] => Value2
            [3] => Value3
        )
    [Key2] => Array
        (
            [0] => Value1
            [1] => Value2
            [3] => Value3
            [4] => Value4
        )
)

e se você deseja reorganizar a ordem das teclas,

foreach($array as $k=>$v){
    $unique= array_values(array_unique($v));
    $array[$k]=$unique;
}

Esta operação fornecerá valores-chave organizados como este:

Array
(
    [Key1] => Array
        (
            [0] => Value1
            [1] => Value2
            [2] => Value3
        )
    [Key2] => Array
        (
            [0] => Value1
            [1] => Value2
            [2] => Value3
            [3] => Value4
        )
)

Espero que isso esclareça tudo.


2

se você tiver uma matriz como esta:

(users é o nome da matriz)

Array=>
 [0] => (array)
   'user' => 'john'
   'age' => '23'
 [1] => (array)
  'user' => 'jane'
  'age' => '20'
 [2]=> (array)
  'user' => 'john'
  'age' => '23'

e você deseja excluir duplicatas ... então:

$serialized = array();
for ($i=0; $i < sizeof($users); $i++) { 
  $test = in_array($users['user'], $serialized);
    if ($test == false) {
      $serialized[] = $users['user'];
    }
 }

pode ser uma solução: P


1

Uma solução de fácil leitura, provavelmente não a mais eficiente:

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

Como as pessoas estão dizendo array_unique()é muito lento, aqui está um trecho que eu uso para uma matriz multidimensional de nível.

$serialized_array = array_map("serialize", $input);

foreach ($serialized_array as $key => $val) {
     $result[$val] = true;
}

$output = array_map("unserialize", (array_keys($result)));

Referência ao primeiro usuário contribuiu com a nota da página de array_unique() funções no php.net


Anuj, você poderia editar sua resposta? Há um erro. Ele deve terminar $output = array_map('unserialize', array_keys($result));
keyboardSmasher

@keyboardSmasher obrigado por sua contribuição. Fiz as alterações e agora funciona. :)
Anuj

1

Muita pessoa me perguntou como criar um array multidimensional exclusivo. Tomei referência do seu comentário e isso me ajuda.

Antes de tudo, obrigado a @jeromegamez @daveilers por sua solução. Mas toda vez que eu dava a resposta, eles me perguntavam como esse 'serializar' e 'desserializar' funciona. É por isso que quero compartilhar o motivo disso com você, para que ajude mais pessoas a entender o conceito por trás disso.

Estou explicando por que usamos 'serialize' e 'unserialize' nas etapas:

Etapa 1: Converter a matriz multidimensional em matriz unidimensional

Para converter a matriz multidimensional em uma matriz unidimensional, primeiro gere representação de fluxo de bytes de todos os elementos (incluindo matrizes aninhadas) dentro da matriz. A função serialize () pode gerar representação de fluxo de bytes de um valor. Para gerar a representação do fluxo de bytes de todos os elementos, chame a função serialize () dentro da função array_map () como uma função de retorno de chamada. O resultado será uma matriz unidimensional, independentemente de quantos níveis a matriz multidimensional possua.

Etapa 2: Tornar os valores exclusivos

Para tornar essa matriz unidimensional exclusiva, use a função array_unique ().

Etapa 3: reverta-o para a matriz multidimensional

Embora a matriz agora seja única, os valores parecem representação de fluxo de bytes. Para reverter para a matriz multidimensional, use a função unserialize ().

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

Mais uma vez obrigado por tudo isso.


0

Uma alternativa para serializar e único

$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

Se você tem uma matriz como esta

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

e você deseja obter matrizes assim:

data = array
(
[0] => array
(
    [subject] => a
    [object] => c
),
[1] => array
(
    [subject] => b
    [object] => d
),
[2] => array
(
    [subject] => d
    [object] => c
)
)

ou

data = array
(
[0] => array
(
    [subject] => d
    [object] => b
),
[1] => array
(
    [subject] => c
    [object] => a
),
[2] => array
(
    [subject] => c
    [object] => d
)
)

um código a seguir pode ajudar

    $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

Pensei bastante neste problema e determinei que a solução ideal deveria seguir duas regras.

  1. Para escalabilidade, modifique a matriz no local; sem copiar para uma nova matriz
  2. Para desempenho, cada comparação deve ser feita apenas uma vez

Com isso em mente e considerando todas as peculiaridades do PHP, abaixo está a solução que eu criei. Ao contrário de algumas das outras respostas, ele tem a capacidade de remover elementos com base nas chaves que você deseja. Espera-se que a matriz de entrada seja teclas numéricas.

$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]);
                }
            }
        }
    }
}

A única desvantagem é que as chaves não estão em ordem quando a iteração é concluída. Isso não é um problema se você estiver usando posteriormente apenas loops foreach, mas se precisar usar um loop for, poderá colocar $input = array_values($input);o que foi dito acima para renumerar as chaves.


0

Com base na resposta marcada como correta, adicionando minha resposta. Código pequeno adicionado apenas para redefinir os índices -

$input = array_values(array_map("unserialize", array_unique(array_map("serialize", $inputArray))));
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.