2010-03-11 27 views
55

Esiste un metodo come array_unique per gli oggetti? Ho un sacco di array con gli oggetti 'ruolo' che unire e quindi voglio prendere i duplicati :)array_unique per gli oggetti?

+1

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

risposta

73

bene, array_unique() confronta il valore stringa degli elementi:

Nota : Due elementi sono considerati uguali se e solo se (string) $elem1 === (string) $elem2 cioè quando la rappresentazione della stringa è la stessa, verrà utilizzato il primo elemento.

Assicurati quindi di implementare il metodo __toString() nella classe e di restituire lo stesso valore per ruoli uguali, ad es.

class Role { 
    private $name; 

    //..... 

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

} 

Questo considererebbe due ruoli uguali se hanno lo stesso nome.

+0

grazie =) sembra funzionare bene per me;) – Johannes

+2

@Jacob perché né 'array_unique' né' __toString() 'mettono a confronto nulla. '__toString()' definisce il comportamento di un'istanza di un oggetto quando viene utilizzato in un contesto di stringa e 'array_unique' restituisce l'array di input con i valori duplicati rimossi. Solo * usa * il confronto per questo internamente. – Gordon

+1

@Jacob Relkin: non è un comparatore. È la rappresentazione di stringa dell'oggetto. Penso che lo usino in quanto è possibile convertire qualsiasi tipo, oggetto, ecc. In una stringa. Ma il metodo stringa stesso su un oggetto non viene utilizzato solo da questa funzione. Per esempio. 'echo $ object' usa anche il metodo' __toString'. –

5

Da qui: http://php.net/manual/en/function.array-unique.php#75307

Questo dovrebbe funzionare con gli oggetti e gli array anche.

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

Grazie mille. –

+0

@Richard Knop, siete i benvenuti, buona fortuna! –

+0

Grazie mille per il post @SilverLight ha votato per questo sforzo ancora una volta – Amjad

22

So che c'è già una risposta accettata, ma ecco un'alternativa.

Contrariamente alla risposta precedente, utilizza in_array() poiché la natura di comparing objects in PHP 5 ci consente di farlo. Facendo uso di questo comportamento di confronto degli oggetti è necessario che l'array solo contenga oggetti, ma sembra che sia il caso qui.

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

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

var_dump($final); 
+1

Funziona bene, potrebbe essere più veloce dell'altro (non lo so davvero) ma userò il tuo perché non devo prendere extra funzione per questo: D – Gigala

+0

Quando si confrontano gli oggetti devono avere la stessa quantità di campi e devono essere identiche coppie chiave/valore da considerare uguali? quello che sto cercando è .... se ho 2 oggetti e uno di loro ha un campo in più, quegli oggetti non saranno considerati "uguali" – ChuckKelly

+1

'in_array' dovrebbe usare il parametro' $ strict'! Altrimenti, stai confrontando gli oggetti usando "==" invece di "===". Maggiori informazioni qui: http://fr2.php.net/manual/fr/function.in-array.php –

-1

array_unique funziona convertendo gli elementi in una stringa e facendo un confronto. A meno che i tuoi oggetti non vengano espressi in modo univoco per le stringhe, non funzioneranno con array_unique.

Invece, implementare una funzione di confronto stateful per i propri oggetti e utilizzare array_filter per eliminare elementi che la funzione ha già visto.

+0

Ero in giro per una soluzione più elegante (una che non richiederebbe callback). Tuttavia, apprezza la tua risposta. –

+1

'array_unique' usato con SORT_REGULAR funziona, vedere la mia risposta qui sotto. –

10

Ecco un modo per rimuovere gli oggetti duplicati in un array:

<?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 risposta qui è ottima e offre una soluzione per gli oggetti che sono istanze di classi personalizzate, ma non vedo come possa funzionare per le istanze di base di stdClass. –

+0

@Emanuil: vero. Ho aggiunto un'altra risposta alla mia risposta. – yankee

+0

Puoi aggiungere qualche commento su questo codice? Non penso di averlo capito. –

6

È inoltre possibile serializzare prima:

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

Dal PHP 5.2.9 si può semplicemente utilizzare opzionale sort_flag SORT_REGULAR:

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

array_unique lavori con una serie di oggetti utilizzando 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)); 

stamperà:

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

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

vederlo in azione qui: http://3v4l.org/VvonH#v529

Avviso: utilizzerà il confronto "==", non il confronto stretto ("===").

Quindi, se si desidera rimuovere i duplicati all'interno di una matrice di oggetti, fare attenzione a confrontare ciascuna proprietà dell'oggetto, non confrontare l'identità dell'oggetto (istanza).

+9

Questa risposta è molto meglio della risposta accettata. Tuttavia l'esempio non mostra la differenza tra il confronto sul valore ('==') o l'identità ('===') a causa di '$ bam-> prop = 'test2';' (dovrebbe essere ''test1'' per mostrare la differenza). Vedi http://codepad.viper-7.com/8NxWhG per un esempio. – Flip

+1

@Flip concordato. Questa risposta è molto meglio. Upvoted. – MikeSchinkel

+0

fantastico .... :) come studiare queste cose per array di oggetti e array normali? – Vishal

5

È inoltre possibile utilizzare la funzione che array_filter, se si desidera filtrare gli oggetti in base a un attributo specifico:

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

modo sano e veloce se avete bisogno di filtrare le istanze duplicate (vale a dire "===" confronto) di matrice e:

  • siete sicuri di cosa array contiene solo oggetti
  • non avete bisogno di chiavi conservate

è:

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