How to make an array of objects have only unique values?

2

If I have a collection with the same object in a array , how could I do to make this collection with only single instance objects?

Example:

$a = new stdClass;

$a->id = 1;

$b = $a; // mesmo objeto

$c = new stdClass;

$c->id = 2;


$colecao = [];

$colecao[] = $a;
$colecao[] = $b;
$colecao[] = $c;

I want to return only objects that are not the same (because $a in this case has the same hash (inner id in php) of $b ).

If I try to do this, I will have an error:

$unicos = array_unique($colecao);
  

Object of class stdClass could not be converted to string

It seems that array_unique is only valid for values of type scalar .

How could I make the above result look similar?

[$a, $c]
    
asked by anonymous 23.07.2015 / 22:36

3 answers

0

Thank you all for the answers, but in this case, I will give my answer, to give some clarification.

How do objects work in PHP?

First, let's reinforce some terms:

  

Object is an instance of a class.

Second, let's clarify something: In PHP, I can have two instances of stdClass ( $a and $b ), but if I equal $c $a , the two will be the same object working in the same instance (what is changed in $c implies change in $a ).

That is:

$a , $b and $c are instances of stdClass , however $c and $a is the same object, since $c refers to $a - in php objects are automatically passed by reference, according to the manual.

Suppose the following scenario:

$a = new stdClass;

$b = new stdClass;

$c = $a;

That:

[$a, $b, $c]

It would be the same as this:

[$a, $b, $a]

For $c to become a new instance of stdClass , regardless of instance stored in $a , we would have to use the keyword clone .

So:

$c = clone $a

SplObjectStorage - The Solution

Then, to return an array of objects with unique instances (instances of the same object, but not repeating these objects), we could do this:

$a = new stdClass;

$a->id = 1;

$b = $a;

$b->id = 2;

$c = new stdClass;

$c->id = 3;

$storage = new SplObjectStorage();

$storage->attach($a);
$storage->attach($b);
$storage->attach($c);

print_r(iterator_to_array($storage));

The result would be:

Array
(
    [0] => stdClass Object
        (
            [id] => 2
        )

    [1] => stdClass Object
        (
            [id] => 3
        )

)

Note that% w_that% was 1, became% w_th of%.

To understand what I say, see what happens in this example:

$a = new stdClass;

$a->id = 1;

$b = $a;

$b->id = 2;

var_dump($a->id, $b->id); // int 2 e int 2

link

Why did this happen?

Because, internally, id , uses the hash of the object as the index to assign it to a 2 . This is done through a function called SplObjectStorage :

See this example:

var_dump(spl_object_hash($a));
var_dump(spl_object_hash($b));
var_dump(spl_object_hash($c));

var_dump(spl_object_hash($a) === spl_object_hash($b));

var_dump(spl_object_hash($a) === spl_object_hash($c));

var_dump(spl_object_hash($b) === spl_object_hash($c));

The result is:

string '0000000076e70b6400007fac140b77fd' (length=32)

string '0000000076e70b6400007fac140b77fd' (length=32)

string '0000000076e70b6500007fac140b77fd' (length=32)

boolean true

boolean false

boolean false

This makes it clear that array and spl_object_hash , are the same. I leave my little contribution there. Thanks for the answers!

Why not array_unique?

Because it does not use the same "uniqueness" rule that $c uses.

See this example:

$a = new stdClass;

$a->id = 2;

$b = new stdClass;

$b->id = 2;

$case1 = array_unique([$a, $b], SORT_REGULAR);

$storage = new SplObjectStorage();

$storage->attach($a);
$storage->attach($b);

$case2 = iterator_to_array($storage);


var_dump($case1, $case2);

View the results:

#array_unique
   array (size=1)
      0 => 
        object(stdClass)[6]
          public 'id' => int 2
#SplObjectStorage com iterator_to_array
    array (size=2)
      0 => 
        object(stdClass)[6]
          public 'id' => int 2
      1 => 
        object(stdClass)[7]
          public 'id' => int 2

Example: link

It seems that $a took into account the fact that SplObjectStorage has the same value in both cases, however, as previously stated, array_unique took into account the internal identification of the object.

If I'm wrong about something, you can correct me :). But that's what I mean by objects in PHP!

    
24.07.2015 / 13:39
5

Try it like this:

$unicos = array_unique($colecao, SORT_REGULAR);

The array_unique () function takes two arguments as arguments (the second parameter is optional) :

array array_unique ( array $array [, int $sort_flags = SORT_STRING ] )
  

array : The array you want to sort.

     

sort_flags : This parameter is optional but is important because it allows you to change the default behavior of the function. It can take one of the following values:

     
  • SORT_REGULAR - "normal" comparison (without change of types)
  •   
  • SORT_NUMERIC - numeric comparison
  •   
  • SORT_STRING - compare items as (default)
  •   
  • SORT_LOCALE_STRING - compare items as strings (LOCALE dependent)
  •   

By default, the function will try to convert the objects to string to make the comparison, hence you see the message:

  

Object of class stdClass could not be converted to string

You can check into action here: link

    
23.07.2015 / 22:56
2

Of course, the response from @bruno is better, but as I already did, I'll post anyway, for didactics.

You could create a function:

function ClearArray($arr, $KeepKey = FALSE){
    if (!is_array($arr)) return FALSE; // Se não for array retorna FALSE
    if (count($arr) == 0) return $arr; // Se estiver vazio retorna-o
    $narr = Array(); // Array de Retorno
    $c = 0; // Novos indices
    foreach($arr as $k => $v){
        // Se mantém o índice original usa-o senão usa o novo
        $k = ($KeepKey ? $k : $c);          
        $keep = TRUE; // Mantém o item

        foreach($narr as $k2 => $v2){ // Percorre o novo array
            // Verifica se já foi adicionado um item igual
            if ($v === $v2){
                $keep = FALSE; // Não mantém o registro
                break;
            }

        }

        if($keep){ // Se mantém o item
            $narr[$k] = $v; // Adiciona-o no novo array
            $c++;
        }

    }

    return $narr; // Retorna
}

Usage:

$a = new stdClass;
$a->id = 1;

$b = $a; // mesmo objeto

$c = new stdClass;
$c->id = 2;

$colecao = Array();

$colecao[] = $a;
$colecao[] = $b;
$colecao[] = $c;

// Array Original
var_dump($colecao);

// Array com itens unicos
$arr = ClearArray($colecao, true);

echo '-------------------------'.PHP_EOL;

var_dump($arr);
$unicos = array_unique($colecao, SORT_REGULAR);

Output:

array(3) {
  [0]=>
  object(stdClass)#1 (1) {
    ["id"]=>
    int(1)
  }
  [1]=>
  object(stdClass)#1 (1) {
    ["id"]=>
    int(1)
  }
  [2]=>
  object(stdClass)#2 (1) {
    ["id"]=>
    int(2)
  }
}
-------------------------
array(2) {
  [0]=>
  object(stdClass)#1 (1) {
    ["id"]=>
    int(1)
  }
  [2]=>
  object(stdClass)#2 (1) {
    ["id"]=>
    int(2)
  }
}
    
23.07.2015 / 23:07