34

Can the in_array function compare objects?

For example, I have an array of objects and I want to add them distinctly to another array. Is it possible to check if an object has already been added like so:

in_array($distinct, $object);

or is there any other way?

5
  • What's your definition of "distinct"? If "distinct" is the opposite of reference equality then simply passing in true as the third parameter will do it. Commented Jun 21, 2013 at 9:36
  • So this will work with objects in array? Commented Jun 21, 2013 at 9:37
  • 1
    For some definition of "work". You need to give more details, mind reading is hard. Commented Jun 21, 2013 at 9:44
  • 2
    Does the answer for similar question helps you? Commented Jun 21, 2013 at 9:49
  • what means distinct, its unclear what you want. Commented Jun 21, 2013 at 10:03

8 Answers 8

38

You can use strict comparison:

in_array($object, $array, TRUE);

Usage example:

$a = new stdClass();
$a->x = 42;

$b = new stdClass();
$b->y = 42;

$c = new stdClass();
$c->x = 42;

$array = array($a,$b);

echo in_array($a, $array, true); // 1
echo in_array($b, $array, true); // 1
echo in_array($c, $array, true); //
Sign up to request clarification or add additional context in comments.

1 Comment

Due to the broad title of the question, I like to add that when you want to find any object from a different class, this won't work. Only finding exactly the same instance of an object works with strict comparison.
16

The in_array function cannot compare objects.

You should create unique key-value pairs from your objects and only need to compare those keys when inserting a new object into your final array.

Assuming that each object has an unique id property, a possible solution would be:

$unique_objects = array();

// $data represents your object collection
foreach ($data as $item) {
    if (!array_key_exists($item->id, $unique_objects)) {
        $unique_objects[$item->id] = $obj;
    }
}

2 Comments

Good point. I've updated the response to fix the $idem typo. The assumption with this sample code is that $obj simply represents an existing variable that needs to be uniquely added to the $unique_objects array.
Of course in_array can compare objects, strictly speaking. This answer should be unaccepted and deleted as it states false infos.
7

I did some tests comparing objects with the in_array function. This is my conclusion:

When you try to find the same instance of an object in an array (like OP), then in_array could work with the strict comparison boolean set.

When you try to find any object of the same class but with a different instance, in_array shows counter-intuitive behavior.

There is a great user comment on PHP.net about the counter-intuitive edge cases.

// Example array

$array = array(
    'egg' => true,
    'cheese' => false,
    'hair' => 765,
    'goblins' => null,
    'ogres' => 'no ogres allowed in this array'
);

// Loose checking -- return values are in comments

// First three make sense, last four do not

in_array(null, $array); // true
in_array(false, $array); // true
in_array(765, $array); // true
in_array(763, $array); // true
in_array('egg', $array); // true
in_array('hhh', $array); // true
in_array(array(), $array); // true

// Strict checking

in_array(null, $array, true); // true
in_array(false, $array, true); // true
in_array(765, $array, true); // true
in_array(763, $array, true); // false
in_array('egg', $array, true); // false
in_array('hhh', $array, true); // false
in_array(array(), $array, true); // false

As you can see that without strict checking the in_array tests 4 - 7 don't make sense.

We know from PHP.net that two objects are only the same in strict comparison (===) when they are from the same class + instance. Two objects are already the same in loose comparison (==) when they are from the same class.

I wrote some tests with objects to see what happens.

$a = new stdClass();                              
$a->egg = true;                                   

$b = new stdClass();                              
$b->cheese = false;                               

$c = new stdClass();                              
$c->hair = 765;                                   

$d = new stdClass();                              
$d->goblins = null;                               

$e = new stdClass();                              
$e->ogres = 'no ogres allowed in this array';     

$array2 = array($a, $b, $c,  $d, $e);         

$e = new stdClass();                                            
$e->egg = null;                                                 

$f = new stdClass();                                            
$f->egg = false;                                                

$g = new stdClass();                                            
$g->egg = 765;                                                  

$h = new stdClass();                                            
$h->egg = 763;                                                  

$i = new stdClass();                                            
$i->egg = 'egg';                                                

$j = new stdClass();                                            
$j->egg = 'hhh';                                                

$k = new stdClass();                                            
$k->egg = array();                                              

in_array($e, $array2, false); // false                
in_array($f, $array2, false); // false                
in_array($g, $array2, false); // true                 
in_array($h, $array2, false); // true                 
in_array($i, $array2, false); // true                 
in_array($j, $array2, false); // true                 
in_array($k, $array2, false); // false                

in_array($e, $array2, true); // false                 
in_array($f, $array2, true); // false                 
in_array($g, $array2, true); // false                 
in_array($h, $array2, true); // false                 
in_array($i, $array2, true); // false                 
in_array($j, $array2, true); // false                 
in_array($k, $array2, true); // false 

In the last checks in_array checks 3 - 6 give counter-intuitive results.

The reason is as follow. If you try to find any object with a certain value, you are forced to use loose comparison (because when the class is not the same, strict comparison always fails). But due to PHP's variable types, in the last tests these checks are considered true, because the value is considered truthy. Also note that the key on the object is ignored in loose comparison.

Comments

3

See http://php.net/manual/en/function.spl-object-hash.php

if ( ! array_key_exists( spl_object_hash( $obj ), $objects ) ) {
    $objects[ spl_object_hash( $obj ) ] = $obj;
}

Cheers

Comments

3

There's numerous ways you can do this as you can see. I just thought I would add another one. I don't know why, but when working with object arrays I like to use the array functions which use callbacks.

If your objects have any sort of identifier, which they should if you want to test them for duplication, the following will work:

$found = array_filter($uniqueObjects, function($uniqueObject) use ($object) {
    return $uniqueObject->id == $object->id
});

if (!$found) {
    $uniqueObjects[] = $object;
}

$object is the object you're looking for, and $uniqueObjects is the array of objects you're searching to see if it exists. Just match uniqueObject and object on an identifying property, such as an id.

Comments

1

I don't know if it is because of a newer PHP version, but in my project, using PHP 5.3.16 on Ubuntu 12.04, it worked. It found the needle object in my array of objects. I have also double-checked by loading a different object of the same class, and testing it against the array contents which did not contain that object, and it returned false indeed.

So yes, in_array can compare objects.

Comments

0

If "STRICT" is "FALSE", the comparison is made by converting in a string the elements. So if you override the __toString magic function, you should be available to compare the objects elements.

Comments

0

I've come up with a somewhat different, I think more robust, option.

function array_add_unique(&$array, $new, $test, $cb) {
  if(is_array($array) && count($array)>0) {
    for($i = 0; $i < count($array); $i++) {
      if( $array[$i][$test] == $new[$test] ) {
        $do = $cb($array[$i], $new);
        if(is_bool($do) && $do) { $array[$i] = $new; }
        else if(!is_bool($do)) { $array[$i] = $do; }
        return;
      }
    }
  }
  array_push($array, $new);
}

The benefit to this solution, is it includes a user definable callback to handle collisions. When your adding unique objects, you may want to preserve properties from both the old and the new object.

The callback, which can be an anonymous function, receives both the new object and the existing object so the user can have a custom calculation. Return true to simply replace the existing object, or return a new object (non-bool) to replace it.

I do not know the performance of this on large datasets though.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.