array_unique für Objekte?


Antworten:


95

Nun, array_unique()vergleicht den String - Wert der Elemente:

Hinweis : Zwei Elemente werden genau dann als gleich angesehen, wenn bei gleicher (string) $elem1 === (string) $elem2Zeichenfolgendarstellung das erste Element verwendet wird.

Stellen Sie daher sicher, dass die __toString()Methode in Ihrer Klasse implementiert ist und dass sie denselben Wert für gleiche Rollen ausgibt, z

class Role {
    private $name;

    //.....

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

}

Dies würde zwei Rollen als gleich betrachten, wenn sie denselben Namen haben.


2
@ Jacob, weil weder array_uniquenoch __toString()etwas vergleichen. __toString()Definiert, wie sich eine Objektinstanz bei Verwendung in einem Zeichenfolgenkontext verhalten soll, und array_uniquegibt das Eingabearray mit entfernten doppelten Werten zurück. Es wird nur intern ein Vergleich verwendet .
Gordon

1
@ Jacob Relkin: Es ist kein Komparator. Es ist die Zeichenfolgendarstellung des Objekts. Ich denke, sie verwenden dies, da Sie jeden Typ, jedes Objekt usw. in eine Zeichenfolge konvertieren können. Die String-Methode selbst für ein Objekt wird jedoch nicht nur von dieser Funktion verwendet. ZB echo $objectverwendet auch die __toStringMethode.
Felix Kling

3
Das Hinzufügen von __toString()Methoden zu all Ihren Objekten ist viel schmerzhafter als das Hinzufügen eines SORT_REGULARFlags zu array_unique, siehe Matthieu Napoli seine Antwort. Außerdem hat eine __toString()Methode viele andere Anwendungsfälle, die dann für den Objektvergleich verwendet werden, so dass dies möglicherweise nicht einmal möglich ist.
Flip

154

array_uniquearbeitet mit einer Reihe von Objekten mit 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));

Wird drucken:

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

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

Sehen Sie es hier in Aktion: http://3v4l.org/VvonH#v529

Warnung : Es wird der Vergleich "==" verwendet, nicht der strikte Vergleich ("===").

Wenn Sie also Duplikate in einem Array von Objekten entfernen möchten, achten Sie darauf, dass die einzelnen Objekteigenschaften verglichen werden und nicht die Objektidentität (Instanz).


12
Diese Antwort ist viel besser als die akzeptierte Antwort. Das Beispiel zeigt jedoch nicht den Unterschied zwischen dem Vergleich von Wert ( ==) oder Identität ( ===) aufgrund von $bam->prop = 'test2';(sollte 'test1'den Unterschied darstellen). Ein Beispiel finden Sie unter codepad.viper-7.com/8NxWhG .
Flip

@vishal werfen Sie einen Blick auf die offizielle Dokumentation: php.net/manual/en/function.array-unique.php
Matthieu Napoli

1
Du hast mich gerettet, Bruder, TnQ sehr: *
Saman Sattari

1
Sollte die akzeptierte Antwort sein. Auch dies viel schneller als mit __toString (). Das Vergleichsergebnis finden Sie unter: sandbox.onlinephpfunctions.com/code/… .
LucaM

1
Achten Sie darauf, Objekte mit array_unique () zu vergleichen. Die Funktion wendet einen umfassenden Vergleich an, der zum Absturz Ihres Servers führen kann, wenn zu viel Rekursion erforderlich ist. Ich sehe Sie als Doctrine-Entitäten. Identifizieren Sie besser, was Ihr Objekt einzigartig macht, und indizieren Sie Ihre Objekte damit. Wenn Ihre Objekte beispielsweise eine Zeichenfolgenkennung haben, erstellen Sie ein Array mit dieser Kennung als Schlüssel.
Olvlvl

31

Diese Antwort wird verwendet, in_array()da die Art des Vergleichens von Objekten in PHP 5 dies ermöglicht. Um dieses Objektvergleichsverhalten nutzen zu können, muss das Array nur Objekte enthalten. Dies scheint hier jedoch der Fall zu sein.

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

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

var_dump($final);

1
Funktioniert gut, es ist vielleicht schneller als das andere (weiß es nicht wirklich), aber ich werde deins verwenden, weil ich keine zusätzliche Funktion dafür übernehmen muss: D
Gigala

Beim Vergleichen von Objekten müssen sie die gleiche Anzahl von Feldern und identische Schlüssel / Wert-Paare haben, um als gleich richtig angesehen zu werden. Was ich
vorhabe

1
in_arraysollte den $strictParameter verwenden! Andernfalls vergleichen Sie Objekte mit "==" anstelle von "===". Mehr hier: fr2.php.net/manual/fr/function.in-array.php
Matthieu Napoli

1
Die Nichtverwendung des strengen Parameters war hier eine bewusste Wahl. Ich wollte "gleiche" Objekte finden, nicht unbedingt die gleiche Instanz eines Objekts. Dies wird in dem in der Antwort erwähnten Link erklärt, der besagt: " Bei Verwendung des Vergleichsoperators (==) werden Objektvariablen auf einfache Weise verglichen, nämlich: Zwei Objektinstanzen sind gleich, wenn sie dieselben Attribute und Werte haben. und sind Instanzen derselben Klasse. "
salathe

Klappt wunderbar! Vielen Dank
Gronaz

16

So entfernen Sie doppelte Objekte in einem 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);

Das ist für mich die beste Lösung! Ich verwende diese Lösung für meine Website searchengine (füge 2 Abfrageergebnisse aus einer Datenbank zusammen). Zuerst habe ich die Ergebnisse für alle Suchbegriffe und füge sie mit den Ergebnissen einiger Suchbegriffe zusammen. Mit dieser Lösung habe ich die wichtigsten Ergebnisse zuerst, hinzugefügt durch einzigartige andere Lösungen.
Finduilas

11

Sie können auch zuerst serialisieren:

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

Ab PHP 5.2.9 können Sie nur optional verwenden sort_flag SORT_REGULAR:

$unique = array_unique( $array, SORT_REGULAR );

9

Sie können auch die Funktion array_filter verwenden, wenn Sie Objekte anhand eines bestimmten Attributs filtern möchten:

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

6

Von hier: http://php.net/manual/en/function.array-unique.php#75307

Dieser würde auch mit Objekten und Arrays arbeiten.

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

2

Wenn Sie über ein indiziertes Array von Objekten verfügen und Duplikate durch Vergleichen einer bestimmten Eigenschaft in jedem Objekt entfernen möchten, kann eine Funktion wie die remove_duplicate_models()folgende verwendet werden.

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

Das Ergebnis ist:

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

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

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

)

0

Dies ist eine sehr einfache Lösung:

$ids = array();

foreach ($relate->posts as $key => $value) {
  if (!empty($ids[$value->ID])) { unset($relate->posts[$key]); }
  else{ $ids[$value->ID] = 1; }
}

-1

array_unique wandelt die Elemente in einen String um und führt einen Vergleich durch. Wenn Ihre Objekte nicht eindeutig in Zeichenfolgen umgewandelt werden, funktionieren sie nicht mit array_unique.

Implementieren Sie stattdessen eine statusbehaftete Vergleichsfunktion für Ihre Objekte und verwenden Sie array_filter , um Dinge wegzuwerfen, die die Funktion bereits gesehen hat.


Ich suchte nach einer eleganteren Lösung (für die keine Rückrufe erforderlich wären). Trotzdem schätzen Sie Ihre Antwort.
Emanuil Rusev

1
array_uniqueverwendet mit SORT_REGULAR funktioniert, siehe meine Antwort unten.
Matthieu Napoli

-1

vernünftiger und schneller Weg, wenn Sie doppelte Instanzen (dh "===" Vergleich) aus dem Array herausfiltern müssen und:

  • Sie sind sicher, welches Array nur Objekte enthält
  • Sie brauchen keine Schlüssel erhalten

ist:

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

-1

Auf diese Weise vergleiche ich Objekte mit einfachen Eigenschaften und erhalte gleichzeitig eine eindeutige Sammlung:

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

Wird ausgegeben:

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

-1

Wenn Sie ein Array von Objekten haben und diese Sammlung filtern möchten, um alle Duplikate zu entfernen, können Sie array_filter mit anonymer Funktion verwenden:

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

Wichtig: Denken Sie daran, dass Sie das $tmpArray als Referenz für Ihre Filter-Rückruffunktion übergeben müssen, da es sonst nicht funktioniert

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.