2010-03-11 13 views
55

¿Hay algún método como array_unique para objetos? Tengo un montón de matrices con objetos 'papel' que se fusionan, y luego quiero llevar a cabo los duplicados :)array_unique para objetos?

+1

duplicados de http://stackoverflow.com/questions/1050709/how-can-i-remove-duplicates-in-an-object-array-in-php/ –

Respuesta

73

Bueno, array_unique() compara el valor de cadena de los elementos:

Nota : Dos elementos se consideran iguales si y solo si (string) $elem1 === (string) $elem2 es decir, cuando la representación de la cadena es la misma, se usará el primer elemento.

Así que asegúrese de implementar el método __toString() en su clase y que muestre el mismo valor para roles iguales, p.

class Role { 
    private $name; 

    //..... 

    public function __toString() { 
     return $this->name; 
    } 

} 

Esto consideraría dos funciones iguales si tienen el mismo nombre.

+0

gracias =) eso parece funcionar bien para mí;) – Johannes

+2

@Jacob porque ni 'array_unique' ni' __toString() 'comparan nada. '__toString()' define cómo se supone que una instancia de objeto se comporta cuando se usa en un contexto de cadena y 'array_unique' devuelve la matriz de entrada con valores duplicados eliminados. Solo * usa * la comparación para esto internamente. – Gordon

+1

@Jacob Relkin: No es un comparador. Es la representación de cadena del objeto. Creo que usan esto ya que puedes convertir cualquier tipo, objeto, etc. en una cadena. Pero el método de cadena en sí mismo en un objeto no solo es utilizado por esta función. P.ej. 'echo $ object' también utiliza el método' __toString'. –

5

A partir de aquí: http://php.net/manual/en/function.array-unique.php#75307

Éste sería trabajar con objetos y matrices también.

<?php 
function my_array_unique($array, $keep_key_assoc = false) 
{ 
    $duplicate_keys = array(); 
    $tmp   = array();  

    foreach ($array as $key=>$val) 
    { 
     // convert objects to arrays, in_array() does not support objects 
     if (is_object($val)) 
      $val = (array)$val; 

     if (!in_array($val, $tmp)) 
      $tmp[] = $val; 
     else 
      $duplicate_keys[] = $key; 
    } 

    foreach ($duplicate_keys as $key) 
     unset($array[$key]); 

    return $keep_key_assoc ? $array : array_values($array); 
} 
?> 
+0

Muchas gracias. –

+0

@Richard Knop, de nada, ¡buena suerte! –

+0

Muchas gracias por esa publicación @SilverLight votó a favor de este esfuerzo gracias una vez más – Amjad

22

Sé que ya hay una respuesta aceptada, pero aquí hay una alternativa.

Contrariamente a la respuesta anterior, usa in_array() ya que la naturaleza de comparing objects en PHP 5 nos permite hacerlo. Hacer uso de este comportamiento de comparación de objetos requiere que la matriz solo contenga objetos, pero ese parece ser el caso aquí.

$merged = array_merge($arr, $arr2); 
$final = array(); 

foreach ($merged as $current) { 
    if (! in_array($current, $final)) { 
     $final[] = $current; 
    } 
} 

var_dump($final); 
+1

Funciona bien, podría ser más rápido que el otro (realmente no lo sé) pero usaré el tuyo porque no tengo que tomar un extra función para ello: D – Gigala

+0

Al comparar objetos, tienen que tener la misma cantidad de campos y deben ser pares de clave/valor idénticos para que se los considere correctos. lo que estoy obteniendo es ... si tengo 2 objetos y uno de ellos tiene un campo adicional, esos objetos no se considerarán "iguales" – ChuckKelly

+1

'in_array' debería usar el parámetro' $ strict'! Si comparas objetos usando "==" en lugar de "===". Más aquí: http://fr2.php.net/manual/fr/function.in-array.php –

-1

array_unique trabaja fundiendo los elementos en una cadena y haciendo una comparación. A menos que tus objetos se unan exclusivamente a cadenas, no funcionarán con array_unique.

En su lugar, implemente una función de comparación con estado para sus objetos y use array_filter para descartar cosas que la función ya ha visto.

+0

Estaba buscando una solución más elegante (una que no requiera devoluciones de llamada). Sin embargo, agradece tu respuesta. –

+1

'array_unique' utilizado con SORT_REGULAR funciona, consulte mi respuesta a continuación. –

10

Aquí es una manera de quitar objetos duplicados en una matriz:

<?php 
// Here is the array that you want to clean of duplicate elements. 
$array = getLotsOfObjects(); 

// Create a temporary array that will not contain any duplicate elements 
$new = array(); 

// Loop through all elements. serialize() is a string that will contain all properties 
// of the object and thus two objects with the same contents will have the same 
// serialized string. When a new element is added to the $new array that has the same 
// serialized value as the current one, then the old value will be overridden. 
foreach($array as $value) { 
    $new[serialize($value)] = $value; 
} 

// Now $array contains all objects just once with their serialized version as string. 
// We don't care about the serialized version and just extract the values. 
$array = array_values($new); 
+1

La respuesta aquí es excelente y ofrece una solución para los objetos que son instancias de clases personalizadas, pero no veo cómo puede funcionar para las instancias básicas de stdClass. –

+0

@Emanuil: cierto. Agregué otra respuesta a mi respuesta. – yankee

+0

¿Puede agregar algunos comentarios sobre este código? No creo que lo entiendo –

6

También puede serializar primera:

$unique = array_map('unserialize', array_unique(array_map('serialize', $array))); 

A partir de PHP 5.2.9 sólo puede utilizar opcional sort_flag SORT_REGULAR:

$unique = array_unique($array, SORT_REGULAR); 
111

array_unique obras con una serie de objetos utilizando SORT_REGULAR:

class MyClass { 
    public $prop; 
} 

$foo = new MyClass(); 
$foo->prop = 'test1'; 

$bar = $foo; 

$bam = new MyClass(); 
$bam->prop = 'test2'; 

$test = array($foo, $bar, $bam); 

print_r(array_unique($test, SORT_REGULAR)); 

imprimirá:

Array (
    [0] => MyClass Object 
     (
      [prop] => test1 
     ) 

    [2] => MyClass Object 
     (
      [prop] => test2 
     ) 
) 

verlo en acción aquí: http://3v4l.org/VvonH#v529

Advertencia: usará la comparación "==", no la comparación estricta ("===").

Por lo tanto, si desea eliminar duplicados dentro de una matriz de objetos, tenga en cuenta que comparará las propiedades de cada objeto, no comparará la identidad del objeto (instancia).

+9

Esta respuesta es mucho mejor que la respuesta aceptada. Sin embargo, el ejemplo no muestra la diferencia entre la comparación en el valor ('==') o la identidad ('===') debido a '$ bam-> prop = 'test2';' (debe ser ''test1'' para mostrar la diferencia). Ver http://codepad.viper-7.com/8NxWhG para un ejemplo. – Flip

+1

@Flip De acuerdo. Esta respuesta es mucho mejor. Upvoted. – MikeSchinkel

+0

awesome .... :) ¿cómo estudiar todas estas cosas para la matriz de objetos y la matriz normal? – Vishal

5

También puede utilizar la función que array_filter, si desea filtrar objetos basado en un atributo específico:

//filter duplicate objects 
$collection = array_filter($collection, function($obj) 
{ 
    static $idList = array(); 
    if(in_array($obj->getId(),$idList)) { 
     return false; 
    } 
    $idList []= $obj->getId(); 
    return true; 
}); 
0

manera sana y rápida si necesita filtrar instancias duplicadas (es decir, "===" comparación) de matriz y:

  • que está seguro de lo que array sólo contiene objetos
  • usted no necesita llaves conservan

es:

//sample data 
$o1 = new stdClass; 
$o2 = new stdClass; 
$arr = [$o1,$o1,$o2]; 

//algorithm 
$unique = []; 
foreach($arr as $o){ 
    $unique[spl_object_hash($o)]=$o; 
} 
$unique = array_values($unique);//optional - use if you want integer keys on output