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