Cómo eliminar valores duplicados de una matriz multidimensional en PHP

306

¿Cómo puedo eliminar valores duplicados de una matriz multidimensional en PHP?

Matriz de ejemplo:

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
    )

)
Ian
fuente

Respuestas:

637

Aquí hay otra manera. No se guardan variables intermedias.

Usamos esto para deduplicar resultados de una variedad de consultas superpuestas.

$input = array_map("unserialize", array_unique(array_map("serialize", $input)));
Daveilers
fuente
23
Debido a la falta de serialización, esto es cada vez más lento cuanto más grande y complejo es el arreglo. Hay una razón por la que usé array_intersect_key (medio año antes de esta respuesta).
OIS
11
@OIS lo acabo de probar, tenía un error tipográfico pero funciona ... ¡gracias amigo !: $ no_duplicates = array_intersect_key ($ array, array_unique (array_map ('serialize', $ array)));
trevorkavanaugh
3
si desea que el índice sea continuo, use valores_arrays, es decir, $ input = valores_arrays (array_map ("unserialize", array_unique (array_map ("serialize", $ input))));
lbsweek
44
Hoy en día, probablemente optaría por json_encode y json_decode en lugar de la serialización PHP. debería tener beneficios para los valores proporcionados y no se encontrará con detalles de serialización de PHP con los que serializar / deserializar envíos es muy probable que no sean deseados.
Hakre
2
Cuidado que serialize(array('a' => '1', 'b' => '1'))es diferente de serialize(array('b' => '1', 'a' => '1')). Esta opción fallará para las matrices utilizadas como setso (hash)maps.
Andras Gyomrey
240

Desde 5.2.9 puede usar array_unique()si usa la SORT_REGULARbandera de esta manera:

array_unique($array, SORT_REGULAR);

Esto hace que la función compare elementos para la igualdad como si $a == $b se estuvieran utilizando, lo cual es perfecto para su caso.

Salida

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

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

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

)

Sin embargo, tenga en cuenta que la documentación dice:

array_unique() no está destinado a funcionar en matrices multidimensionales.

Jack
fuente
2
¡Supongo que esta es una solución más rápida y más clara que la aceptada! ¡votemos por este! :) Hmmm en el sitio php podemos ver que no es tan rápido, como pensé ...
Andron
44
Es extraño que usar el indicador SORT_REGULAR simplemente no funcione para mí, para eliminar matrices duplicadas.
Stefan
44
@Stefan Tienes razón; no parece dar los resultados correctos, pero probablemente sea un error porque funciona con PHP 7 = /
Ja͢ck
44
Esto también parece funcionar en mi caso, pero ¿alguien más está molesto por esta nota en el documento array_unique ()? php.net/manual/en/…
Arleigh Hix
2
@Jack Tienes razón, este es un error a partir de PHP 5.6.23: eval.in/645675 pero se corrigió a partir de PHP 7.0.8: eval.in/645676
Zack Morris
63

Tuve un problema similar pero encontré una solución 100% funcional.

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

?>
Rajendrasinh
fuente
1
Esto responde a una pregunta diferente. Ver aquí: stackoverflow.com/questions/4585208/…
OIS
Gran función! y en caso de que esté tratando con objetos: if (! isset ($ array -> $ v -> $ key)) $ array [$ v -> $ key] = & $ v;
Playnox
35

De otra manera. Conservará las llaves también.

function array_unique_multidimensional($input)
{
    $serialized = array_map('serialize', $input);
    $unique = array_unique($serialized);
    return array_intersect_key($input, $unique);
}
OIS
fuente
Para matrices grandes, este método suele ser al menos un 50% más rápido que la respuesta aceptada.
Lorien Brune
21

Los comentarios de los usuarios sobre la documentación de array_unique () tienen muchas soluciones para esto. Aqui esta uno de ellos:

kenrbnsn en rbnsn dot com
27-sep-2005 12:09

Sin embargo, otro Array_Unique para matrices multi-demensionadas. Solo he probado esto en matrices de dos dimensiones, pero probablemente podría generalizarse para más, o hacer que se use la recursividad.

Esta función utiliza las funciones serializar, array_unique y deserializar para hacer el trabajo.


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

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

Jeremy Ruten
fuente
18

Si "eliminar duplicados" significa "eliminar duplicados, pero deje uno allí", una solución podría ser aplicar array_unique(...)primero en la "columna de identificación" y luego eliminar en la matriz original todas las claves que se han eliminado de la matriz de columnas :

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

El resultado es:

Array
(
    [0] => Array
        (
            [id] => 123
            [foo] => aaa
            [bar] => bbb
        )

    [2] => Array
        (
            [id] => 567
            [foo] => eee
            [bar] => fff
        )

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

Esto eliminará los nombres duplicados de la matriz. único por clave

Mahak Choudhary
fuente
Asegúrese de que $arraylas teclas comiencen en "0". Es posible que $arraylas teclas comiencen en otro número si $arrayes el resultado de una manipulación previa de la matriz. Use array_valuespara restablecer las teclas de nuevo a "0"
stevevance
5

Solución simple:

array_unique($array, SORT_REGULAR)
Saravanan DS
fuente
4

Simplemente use la opción SORT_REGULAR como segundo parámetro.

$uniqueArray = array_unique($array, SORT_REGULAR);
anghazi ghermezi
fuente
1
SORT_REGULAR solo funciona en PHP 7 porque PHP 5 tiene un error (aunque @ r3wt es correcto según la documentación), vea mi comentario en la respuesta para un ejemplo ejecutable stackoverflow.com/questions/307674/…
Zack Morris
¿Por qué agregarías esto? Es lo mismo que esta respuesta, que es más de un año mayor que la suya: stackoverflow.com/a/18373723/870729
random_user_name el
3

si necesita eliminar duplicados en claves específicas, como una identificación de mysqli, aquí hay una función simple

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

Puntos de bonificación Puede pasar una serie de claves y agregar un foreach externo, pero será 2 veces más lento por clave adicional.

r3wt
fuente
3

Una forma muy fácil y lógica de crear una matriz multidimensional única es la siguiente:

Si tienes una matriz como esta:

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

utilizar foreachpara resolver esto:

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

te dará el siguiente resultado:

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

y si quieres reorganizar el orden de las teclas,

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

Esta operación le dará valores clave organizados como este:

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

Espero que esto aclare todo.

Anand agrawal
fuente
2

si tienes una matriz como esta:

(usuarios es el nombre de la matriz)

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

y quieres eliminar duplicados ... entonces:

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

puede ser una solución: P

Limon
fuente
1

Una solución fácil de leer, probablemente no la más 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;

} 
pixeline
fuente
1

Como la gente dice que array_unique()es muy lento, aquí hay un fragmento que uso para una matriz multidimensional de un nivel.

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

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

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

Referencia del primer usuario contribuido nota de la página de array_unique() funciones en php.net

Anuj
fuente
Anuj, ¿podrías editar tu respuesta? Hay un error Debería terminar $output = array_map('unserialize', array_keys($result));
keyboardSmasher
@keyboardSmasher gracias por tu aporte. Hice los cambios y ahora funciona. :)
Anuj
1

Mucha gente me preguntó cómo hacer una matriz multidimensional única. He tomado referencia de tu comentario y me ayuda.

En primer lugar, gracias a @jeromegamez @daveilers por su solución. Pero cada vez que respondía, me preguntaban cómo funciona este 'serializar' y 'no serializar'. Es por eso que quiero compartir la razón de esto con usted para que ayude a más personas a comprender el concepto detrás de esto.

Estoy explicando por qué usamos 'serializar' y 'deserializar' en pasos:

Paso 1: Convierta la matriz multidimensional en una matriz unidimensional

Para convertir la matriz multidimensional en una matriz unidimensional, primero genere una representación de flujo de bytes de todos los elementos (incluidas las matrices anidadas) dentro de la matriz. La función serialize () puede generar una representación de flujo de bytes de un valor. Para generar una representación de flujo de bytes de todos los elementos, llame a la función serialize () dentro de la función array_map () como una función de devolución de llamada. El resultado será una matriz unidimensional sin importar cuántos niveles tenga la matriz multidimensional.

Paso 2: haga que los valores sean únicos

Para hacer que esta matriz unidimensional sea única, use la función array_unique ().

Paso 3: revertirlo a la matriz multidimensional

Aunque la matriz ahora es única, los valores parecen una representación de flujo de bytes. Para revertirlo a la matriz multidimensional, use la función unserialize ().

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

Gracias de nuevo por todo esto.

Manish
fuente
0

Una alternativa para serializar y única

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

* /

Denis Laliberté
fuente
0

Si tienes una 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
)
)

y quieres obtener matrices como esta:

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

o

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

un siguiente código puede ayudar

    $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;
milic
fuente
0

He pensado mucho en este problema y he determinado que la solución óptima debe seguir dos reglas.

  1. Para la escalabilidad, modifique la matriz en su lugar; sin copiar a una nueva matriz
  2. Para el rendimiento, cada comparación debe hacerse solo una vez

Con eso en mente y teniendo en cuenta todas las peculiaridades de PHP, a continuación se muestra la solución que se me ocurrió. A diferencia de algunas de las otras respuestas, tiene la capacidad de eliminar elementos en función de las claves que desee. Se espera que la matriz de entrada sean 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]);
                }
            }
        }
    }
}

El único inconveniente es que las teclas no están en orden cuando se completa la iteración. Esto no es un problema si posteriormente usa solo bucles foreach, pero si necesita usar un bucle for, puede colocar $input = array_values($input);después de lo anterior para volver a numerar las teclas.

Serpiente
fuente
0

Basado en la respuesta marcada como correcta, agregando mi respuesta. Pequeño código agregado solo para restablecer los índices

$input = array_values(array_map("unserialize", array_unique(array_map("serialize", $inputArray))));
Gagan
fuente