array_unique cho các đối tượng?


Câu trả lời:


97

Vâng, array_unique()so sánh giá trị chuỗi của các phần tử:

Lưu ý : Hai phần tử được coi là bằng nhau nếu và chỉ khi (string) $elem1 === (string) $elem2nghĩa là khi biểu diễn chuỗi giống nhau, phần tử đầu tiên sẽ được sử dụng.

Vì vậy, hãy đảm bảo triển khai __toString()phương thức trong lớp của bạn và nó xuất ra cùng một giá trị cho các vai trò như nhau, ví dụ:

class Role {
    private $name;

    //.....

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

}

Điều này sẽ coi hai vai trò như nhau nếu chúng có cùng tên.


2
@Jacob bởi vì không array_uniquevà cũng không __toString()so sánh bất cứ điều gì. __toString()xác định cách một cá thể đối tượng phải hoạt động khi được sử dụng trong ngữ cảnh chuỗi và array_uniquetrả về mảng đầu vào với các giá trị trùng lặp đã bị loại bỏ. Nó chỉ sử dụng so sánh cho điều này trong nội bộ.
Gordon,

1
@Jacob Relkin: Nó không phải là bộ so sánh. Nó là biểu diễn chuỗi của đối tượng. Tôi nghĩ rằng họ sử dụng điều này vì bạn có thể chuyển đổi bất kỳ kiểu, đối tượng, v.v. nào thành một chuỗi. Nhưng bản thân phương thức chuỗi trên một đối tượng không chỉ được sử dụng bởi hàm này. Vd: echo $objectcũng sử dụng __toStringphương pháp.
Felix Kling

3
Thêm __toString()phương thức cho tất cả các đối tượng của bạn sẽ khó hơn nhiều, sau đó chỉ cần thêm một SORT_REGULARcờ vào array_unique, hãy xem câu trả lời của Matthieu Napoli. Bên cạnh một __toString()phương thức có nhiều trường hợp sử dụng khác sau đó được sử dụng để so sánh đối tượng, vì vậy điều này thậm chí có thể không thực hiện được.
Lật

154

array_uniquehoạt động với một mảng các đối tượng bằng cách sử dụng 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));

Sẽ in:

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

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

Xem nó hoạt động tại đây: http://3v4l.org/VvonH#v529

Cảnh báo : nó sẽ sử dụng so sánh "==", không phải so sánh chặt chẽ ("===").

Vì vậy, nếu bạn muốn loại bỏ các bản sao bên trong một mảng đối tượng, hãy cẩn thận rằng nó sẽ so sánh từng thuộc tính đối tượng, không so sánh danh tính đối tượng (cá thể).


12
Câu trả lời này tốt hơn nhiều so với câu trả lời được chấp nhận. Tuy nhiên, ví dụ này không hiển thị sự khác biệt giữa so sánh trên value ( ==) hoặc ID ( ===) vì $bam->prop = 'test2';(phải là 'test1'để thể hiện sự khác biệt). Xem codepad.viper-7.com/8NxWhG để làm ví dụ.
Lật

@vishal có một cái nhìn tại các tài liệu chính thức: php.net/manual/en/function.array-unique.php
Matthieu Napoli

1
bạn đã cứu tôi bro, TnQ rất nhiều: *
Saman Sattari

1
Nên là câu trả lời được chấp nhận. Cũng nhanh hơn rất nhiều so với sử dụng __toString (). Xem: sandbox.onlinephpfunctions.com/code/… để biết kết quả so sánh.
LucaM

1
Chú ý so sánh các đối tượng với array_unique (), hàm sẽ áp dụng một phép so sánh sâu, điều này có thể dẫn đến việc máy chủ của bạn gặp sự cố nếu nó liên quan đến quá nhiều đệ quy — tôi đang xem xét các thực thể Doctrine. Xác định tốt hơn điều gì làm cho đối tượng của bạn trở nên độc đáo và lập chỉ mục các đối tượng của bạn với nó. Ví dụ: nếu các đối tượng của bạn có số nhận dạng chuỗi, hãy xây dựng một mảng với số nhận dạng đó làm khóa.
olvlvl

31

Câu trả lời này sử dụng in_array()vì bản chất của việc so sánh các đối tượng trong PHP 5 cho phép chúng ta làm như vậy. Việc sử dụng hành vi so sánh đối tượng này yêu cầu mảng chỉ chứa các đối tượng, nhưng điều đó dường như là trường hợp ở đây.

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

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

var_dump($final);

1
Các công việc tốt đẹp, nó có thể là nhanh hơn so với người kia (dont thực sự biết) nhưng tôi sẽ sử dụng của bạn, vì tôi không phải đi làm một chức năng bổ sung cho nó: D
Gigala

Khi so sánh các đối tượng, chúng phải có cùng số lượng trường và phải là các cặp khóa / giá trị giống hệt nhau thì mới được coi là giống nhau đúng không? những gì im nhận xét là .... nếu tôi có 2 đối tượng và một trong số họ có một trường bổ sung sẽ những đối tượng không được coi là "tương tự"
ChuckKelly

1
in_array nên sử dụng $strict tham số! Nếu không, bạn so sánh các đối tượng bằng cách sử dụng "==" thay vì "===". Xem thêm tại đây: fr2.php.net/manual/fr/ Chức năng.in
Matthieu Napoli

1
Không sử dụng tham số nghiêm ngặt là một lựa chọn có chủ ý ở đây. Tôi muốn tìm các đối tượng "bằng nhau", không nhất thiết phải giống phiên bản của một đối tượng. Điều này được giải thích trong liên kết được đề cập trong câu trả lời, " Khi sử dụng toán tử so sánh (==), các biến đối tượng được so sánh theo cách đơn giản, cụ thể là: Hai trường hợp đối tượng bằng nhau nếu chúng có cùng thuộc tính và giá trị, và là các phiên bản của cùng một lớp. "
chào

Hoạt động như một sự quyến rũ! Cảm ơn rất nhiều
Gronaz

16

Đây là một cách để loại bỏ các đối tượng trùng lặp trong một mảng:

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

Đây là giải pháp tốt nhất cho tôi! Tôi đang sử dụng giải pháp này cho trang web searchhengine của mình (kết hợp 2 kết quả truy vấn từ cơ sở dữ liệu). Đầu tiên, tôi có các resuls cho tất cả các công cụ tìm kiếm và tôi hợp nhất chúng với kết quả của một số công cụ tìm kiếm. Với giải pháp này, tôi có kết quả quan trọng nhất đầu tiên, bổ sung bởi các giải pháp khác độc đáo ..
Finduilas

11

Bạn cũng có thể sắp xếp thứ tự trước:

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

Kể từ PHP 5.2.9, bạn chỉ có thể sử dụng tùy chọn sort_flag SORT_REGULAR:

$unique = array_unique( $array, SORT_REGULAR );

9

Bạn cũng có thể sử dụng hàm array_filter, nếu bạn muốn lọc các đối tượng dựa trên một thuộc tính cụ thể:

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

Từ đây: http://php.net/manual/en/ Chức năng.array-unique.php#75307

Cái này cũng sẽ hoạt động với các đối tượng và mảng.

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

Nếu bạn có một mảng đối tượng được lập chỉ mục và bạn muốn loại bỏ các bản sao bằng cách so sánh một thuộc tính cụ thể trong mỗi đối tượng, bạn remove_duplicate_models()có thể sử dụng một hàm như hàm dưới đây.

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

Kết quả là:

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

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

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

)

0

một cách lành mạnh và nhanh chóng nếu bạn cần lọc các trường hợp trùng lặp (tức là so sánh "===") ra khỏi mảng và:

  • bạn chắc chắn mảng nào chỉ chứa các đối tượng
  • bạn không cần khóa được bảo quản

Là:

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

Đây là giải pháp rất đơn giản:

$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 hoạt động bằng cách chuyển các phần tử thành một chuỗi và thực hiện so sánh. Trừ khi các đối tượng của bạn được truyền duy nhất thành chuỗi, thì chúng sẽ không hoạt động với array_unique.

Thay vào đó, hãy triển khai một hàm so sánh trạng thái cho các đối tượng của bạn và sử dụng array_filter để loại bỏ những thứ mà hàm đã thấy.


Tôi đã tìm kiếm một giải pháp thanh lịch hơn (một giải pháp không yêu cầu gọi lại). Tuy nhiên, đánh giá cao câu trả lời của bạn.
Emanuil Rusev

1
array_uniqueđược sử dụng với SORT_REGULAR hoạt động, hãy xem câu trả lời của tôi bên dưới.
Matthieu Napoli

-1

Đây là cách của tôi để so sánh các đối tượng có thuộc tính đơn giản và đồng thời nhận được một bộ sưu tập duy nhất:

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

Sẽ xuất:

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

-1

Nếu bạn có mảng đối tượng và bạn muốn lọc bộ sưu tập này để loại bỏ tất cả các bản sao, bạn có thể sử dụng array_filter với chức năng ẩn danh:

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

Quan trọng: Hãy nhớ rằng bạn phải chuyển $tmpmảng làm tham chiếu để lọc hàm gọi lại nếu không nó sẽ không hoạt động

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.