web-dev-qa-db-fra.com

array_unique pour les objets?

Existe-t-il une méthode comme array_unique pour les objets? J'ai un groupe de tableaux avec des objets 'Role' que je fusionne, puis je veux supprimer les doublons :)

66
Johannes

Eh bien, array_unique() compare la valeur de chaîne des éléments:

Note: Deux éléments sont considérés comme égaux si et seulement si (string) $elem1 === (string) $elem2, c'est-à-dire lorsque la représentation sous forme de chaîne est identique, le premier élément sera utilisé.

Veillez donc à implémenter la méthode __toString() dans votre classe et à ce qu’elle affiche la même valeur pour des rôles égaux, par exemple.

class Role {
    private $name;

    //.....

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

}

Cela considérerait deux rôles comme égaux s'ils ont le même nom.

78
Felix Kling

array_unique fonctionne avec un tableau d'objets utilisant 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));

Imprimera:

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

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

Voir en action ici: http://3v4l.org/VvonH#v529

Warning: il utilisera la comparaison "==", pas la comparaison stricte ("===").

Ainsi, si vous souhaitez supprimer les doublons dans un tableau d'objets, sachez qu'il va comparer les propriétés de chaque objet et non comparer l'identité de l'objet (instance).

127
Matthieu Napoli

Je sais qu'il y a déjà une réponse acceptée, mais voici une alternative. 

Contrairement à la réponse précédente, il utilise in_array() puisque la nature de comparer des objets in PHP 5 nous le permet. L'utilisation de ce comportement de comparaison d'objet nécessite que le tableau only contienne des objets, mais cela semble être le cas ici.

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

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

var_dump($final);
24
salathe

Voici un moyen de supprimer les objets en double dans un tableau:

<?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);
13
yankee

Vous pouvez également utiliser la fonction array_filter si vous souhaitez filtrer des objets en fonction d'un attribut spécifique:

//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;
});
8
Arvid Vermote

Vous pouvez aussi d'abord sérialiser:

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

À partir de PHP 5.2.9, vous pouvez simplement utiliser le sort_flag SORT_REGULAR optionnel:

$unique = array_unique( $array, SORT_REGULAR );
7
Remon

De là: http://php.net/manual/en/function.array-unique.php#75307

Celui-ci fonctionnerait également avec des objets et des tableaux.

<?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);
}
?>
6
Silver Light

Si vous avez un tableau indexé d'objets et que vous souhaitez supprimer les doublons en comparant une propriété spécifique dans chaque objet, vous pouvez utiliser une fonction comme celle de remove_duplicate_models() ci-dessous.

class Car {
    private $model;

    public function __construct( $model ) {
        $this->model = $model;
    }

    public function get_model() {
        return $this->model;
    }
}

$cars = [
    new Car('Mustang'),
    new Car('F-150'),
    new Car('Mustang'),
    new Car('Taurus'),
];

function remove_duplicate_models( $cars ) {
    $models = array_map( function( $car ) {
        return $car->get_model();
    }, $cars );

    $unique_models = array_unique( $models );

    return array_values( array_intersect_key( $cars, $unique_models ) );
}

print_r( remove_duplicate_models( $cars ) );

Le résultat est:

Array
(
    [0] => Car Object
        (
            [model:Car:private] => Mustang
        )

    [1] => Car Object
        (
            [model:Car:private] => F-150
        )

    [2] => Car Object
        (
            [model:Car:private] => Taurus
        )

)
1
Kellen Mace

C’est ma façon de comparer des objets avec des propriétés simples tout en recevant une collection unique:

class Role {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

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

$roles = [
    new Role('foo'),
    new Role('bar'),
    new Role('foo'),
    new Role('bar'),
    new Role('foo'),
    new Role('bar'),
];

$roles = array_map(function (Role $role) {
    return ['key' => $role->getName(), 'val' => $role];
}, $roles);

$roles = array_column($roles, 'val', 'key');

var_dump($roles);

Est-ce que la sortie:

array (size=2)
  'foo' => 
    object(Role)[1165]
      private 'name' => string 'foo' (length=3)
  'bar' => 
    object(Role)[1166]
      private 'name' => string 'bar' (length=3)
0
Gander

manière saine et rapide si vous devez filtrer les instances dupliquées (comparaison "===") hors du tableau et

  • vous êtes sûr de savoir quel tableau ne contient que des objets
  • vous n'avez pas besoin de clés conservées

est:

//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
0
Roman Bulgakov

Si vous avez un tableau d'objets et que vous souhaitez filtrer cette collection pour supprimer tous les doublons, vous pouvez utiliser array_filter avec une fonction anonyme:

$myArrayOfObjects = $myCustomService->getArrayOfObjects();

// This is temporary array
$tmp = [];
$arrayWithoutDuplicates = array_filter($myArrayOfObjects, function ($object) use (&$tmp) {
    if (!in_array($object->getUniqueValue(), $tmp)) {
        $tmp[] = $object->getUniqueValue();
        return true;
    }
    return false;
});

Important: N'oubliez pas que vous devez passer le tableau $tmp comme référence à votre fonction de rappel du filtre, sinon cela ne fonctionnera pas

0