array_unique для об'єктів?


Відповіді:


98

Ну, array_unique()порівнює значення рядків елементів:

Примітка : Два елементи вважаються рівними тоді і лише тоді, (string) $elem1 === (string) $elem2коли тобто, коли подання рядка однакове, буде використаний перший елемент.

Тож переконайтеся, що __toString()метод реалізований у вашому класі, і він виводить одне і те ж значення для рівних ролей, наприклад

class Role {
    private $name;

    //.....

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

}

Це розглядало б дві ролі як рівні, якщо вони мали однакове ім'я.


2
@Jacob, бо нічого, array_uniqueні __toString()порівняння нічого. __toString()визначає, як повинен поводитися екземпляр об'єкта при використанні в контексті рядка, і array_uniqueповертає вхідний масив із видаленими повторюваними значеннями. Він просто використовує порівняння для цього внутрішньо.
Гордон,

1
@Jacob Relkin: Це не компаратор. Це рядкове представлення об’єкта. Я думаю, що вони використовують це, оскільки ви можете перетворити будь-який тип, об’єкт тощо у рядок. Але сам метод рядка на об'єкті використовується не тільки цією функцією. Наприклад, echo $objectтакож використовується __toStringметод.
Фелікс Клінг,

3
Додавання __toString()методів до всіх ваших об’єктів набагато болючіше, ніж просто додавання SORT_REGULARпрапора до array_unique, див. Його відповідь Матьє Наполі. Крім того, __toString()метод має багато інших випадків використання, які потім використовуються для порівняння об'єктів, тому це може бути навіть неможливим.
Фліп

156

array_uniqueпрацює з масивом об'єктів, використовуючи 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));

Друкує:

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

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

Подивіться це в дії тут: http://3v4l.org/VvonH#v529

Попередження : використовуватиметься порівняння "==", а не суворе порівняння ("===").

Отже, якщо ви хочете видалити дублікати всередині масиву об’єктів, пам’ятайте, що він буде порівнювати властивості кожного об’єкта, а не порівнювати ідентичність об’єкта (екземпляра).


12
Ця відповідь набагато краща, ніж прийнята відповідь. Однак приклад не показує різниці між порівнянням за значенням ( ==) чи ідентичністю ( ===) через $bam->prop = 'test2';(має бути 'test1'для демонстрації різниці). Для прикладу див. Codepad.viper-7.com/8NxWhG .
Фліп

@vishal подивись офіційну документацію: php.net/manual/en/function.array-unique.php
Matthieu Napoli

1
ти дуже врятував мене, брат, TnQ: *
Саман Саттарі

1
Має бути прийнятою відповіддю. Крім того, це набагато швидше, ніж використання __toString (). Див .: sandbox.onlinephpfunctions.com/code/... для порівняння результатів.
LucaM

1
Слідкуйте за порівнянням об’єктів з array_unique (), функція застосує глибоке порівняння, яке може призвести до аварійного завершення роботи вашого сервера, якщо воно включає занадто багато рекурсії - я розглядаю вас доктринових сутностей. Краще визначте, що робить ваш об’єкт унікальним, та індексуйте об’єкти за допомогою нього. Наприклад, якщо ваші об’єкти мають ідентифікатор рядка, створіть масив із цим ідентифікатором як ключем.
olvlvl

31

Ця відповідь використовується, in_array()оскільки природа порівняння об’єктів у PHP 5 дозволяє нам це робити. Для використання цієї поведінки порівняння об’єктів потрібно, щоб масив містив лише об’єкти, але це, мабуть, має місце тут.

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

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

var_dump($final);

1
Працює добре, це може бути швидше, ніж інший (насправді не знаю), але я скористаюся вашим, тому що мені не потрібно робити додаткову функцію для нього: D
Гігала

При порівнянні об'єктів вони повинні мати однакову кількість полів і повинні бути однаковими парами ключ / значення, щоб вважатися однаковими правильними? до чого я приїжджаю .... якщо в мене є 2 об’єкти, а в одного з них є одне зайве поле, ці об’єкти не будуть вважатися «однаковими»
ChuckKelly

1
in_arrayслід використовувати $strictпараметр! В іншому випадку ви порівнюєте об'єкти, використовуючи "==" замість "===". Більше тут: fr2.php.net/manual/fr/function.in-array.php
Matthieu Napoli

1
Невикористання строгого параметра тут було навмисним вибором. Я хотів знайти "рівні" об'єкти, не обов'язково однаковий екземпляр об'єкта. Це пояснюється у посиланні, згаданому у відповіді, де сказано: " При використанні оператора порівняння (==) змінні об'єкта порівнюються просто, а саме: Два екземпляри об'єкта рівні, якщо вони мають однакові атрибути та значення, і є екземплярами одного класу ".
Салате

Працює як шарм! Велике спасибі
гроназ

17

Ось спосіб видалення повторених об’єктів з масиву:

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

Це для мене найкраще рішення! Я використовую це рішення для пошуку веб-сайту (об’єднати 2 результати запиту з бази даних). Спочатку у мене є результати для всіх пошукових термінів, і я об’єдную їх з результатами деяких пошукових термінів. За допомогою цього рішення я маю найважливіші результати спочатку, додані унікальними іншими рішеннями ..
Finduilas

11

Ви також можете спершу здійснити серіалізацію:

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

Станом на PHP 5.2.9 ви можете просто використовувати необов’язкові sort_flag SORT_REGULAR:

$unique = array_unique( $array, SORT_REGULAR );

9

Ви також можете використовувати функцію array_filter, якщо ви хочете фільтрувати об'єкти на основі певного атрибута:

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

Звідси: http://php.net/manual/en/function.array-unique.php#75307

Цей також буде працювати з об'єктами та масивами.

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

Якщо у вас є індексований масив об’єктів, і ви хочете видалити дублікати, порівнявши певну властивість у кожному об’єкті, remove_duplicate_models()можна використовувати таку функцію, як наведена нижче.

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

Результат:

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

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

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

)

0

розумний і швидкий спосіб, якщо вам потрібно відфільтрувати дубльовані екземпляри (тобто порівняння "===") з масиву та:

  • Ви впевнені, який масив містить лише об'єкти
  • Вам не потрібні ключі, збережені

це:

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

Це дуже просте рішення:

$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 працює, перекидаючи елементи в рядок і виконуючи порівняння. Якщо ваші об'єкти однозначно не передані в рядки, вони не працюватимуть з array_unique.

Натомість реалізуйте функцію порівняння для ваших об’єктів та використовуйте array_filter, щоб викинути те, що функція вже бачила.


Я сподівався на більш елегантне рішення (таке, що не вимагає зворотних дзвінків). Тим не менше, цінуємо вашу відповідь.
Емануїл Русєв

1
array_uniqueвикористовується для робіт SORT_REGULAR, див. мою відповідь нижче.
Matthieu Napoli

-1

Це мій спосіб порівнювати об’єкти з простими властивостями і одночасно отримувати унікальну колекцію:

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

Виведе:

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

-1

Якщо у вас є масив об’єктів, і ви хочете відфільтрувати цю колекцію, щоб видалити всі дублікати, ви можете використовувати array_filter з анонімною функцією:

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

Важливо: Пам'ятайте, що ви повинні передавати $tmpмасив як посилання на вашу функцію фільтрації зворотного виклику, інакше вона не буде працювати

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.