"Array to string conversion error" when calling array_diff_assoc() with a multidimensional array

I get array to string conversion error for the following line:

$diff = array_diff_assoc($stockist, $arr);

Here, $arr is an array decoded from a JSON file. Using the is_array() function I was able to verify that both parameters are arrays. Can someone point me the problem

$stockist = array();
while (!feof($file_handle)) {


$line_of_text = fgetcsv($file_handle);
$query = "SELECT * FROM reorderchart WHERE medicine = '"
. trim($line_of_text[3])
. "' ORDER BY medicine";
$result = mysql_query($query);


if (trim($line_of_text[2]) - trim($line_of_text[1]) <= 0) {
        

while ($row = mysql_fetch_array($result)) {


$file = "results.json";
$arr = json_decode(file_get_contents($file),true);
$pharmacy = trim($row['Medicine']);


if (isset($stockist[$pharmacy])) {
                    

$medicine = $stockist[$pharmacy];
$medicine[] = trim($row['Stockist']);
$stockist[$pharmacy] = $medicine;


} else {


$medicine = array();
$medicine[] = trim($row['Stockist']);
$stockist[$pharmacy] = $medicine;
}
}
}
}
$diff = array();
$diff = array_diff_assoc($stockist,$arr);
ksort($diff);
foreach ($diff as $key => $value) {


echo "<table align='center' border='1'>";
echo "<tr><td align = 'center'> <font color = 'blue'> $key</td></tr>";
    

foreach($value as $key1 => $value1) {


echo "<tr><td align ='center'>$value1</td></tr><br>";
}
echo "</table>";
}
55786 次浏览

According to PHP documentation for the function

Note: Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In words: when the string representation is the same.

For more information refer to http://php.net/manual/en/function.array-diff.php

According to it:

php -r 'array_diff(array("a" => array("b" => 4)), array(1));'
PHP Notice:  Array to string conversion in Command line code on line 1
PHP Stack trace:
PHP   1. {main}() Command line code:0
PHP   2. array_diff() Command line code:1

One of your arrays is multidimensional.

array_diff only checks one dimension of a n-dimensional array. Of course you can check deeper dimensions by using array_diff($array1[0], $array2[0]);

I've got the same error and found the following bug report for php:

https://bugs.php.net/bug.php?id=60198

Some of the array_* functions that compare elements in multiple arrays do so by (string)$elem1 === (string)$elem2.

If $elem1 or $elem2 is an array, then the array to string notice is thrown.

Two examples of functions that can throw this are array_intersect() and array_diff().

If these functions are not expected to take arrays with other arrays as values, this should be mentioned on the documentation pages.

That report describes, why php throws an error on comparing a multi-dimensional array.

Yes, the strict answer is because "One of your arrays is multidimensional."

Another useful note might be - depending on your needs of further parsing the actual differences - consider first testing your arrays with:

$diff = strcmp(json_encode($stockist), json_encode($arr));

or

$diff = strspn(json_encode($stockist) ^ json_encode($arr), "\0");

or

$diff = xdiff_string_diff(json_encode($stockist), json_encode($arr));

All these options will compare the entire array tree, not just the top level.

Since array_diff can only deals with one dimension, you can either:

You can see in the array_diff() documentation that:

Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In other words: when the string representation is the same.

So it looks like you can't use this function with multi dimensional array, or in fact any value that cannot be converted to a string. This is because the function will cast values to a string to do the comparison.

You can write your own function to recursively check arrays for a difference - in fact the following is from the comments of the docs linked above.

You can see the comment here.

function arrayRecursiveDiff($aArray1, $aArray2) {
$aReturn = array();


foreach ($aArray1 as $mKey => $mValue) {
if (array_key_exists($mKey, $aArray2)) {
if (is_array($mValue)) {
$aRecursiveDiff = arrayRecursiveDiff($mValue, $aArray2[$mKey]);
if (count($aRecursiveDiff)) { $aReturn[$mKey] = $aRecursiveDiff; }
} else {
if ($mValue != $aArray2[$mKey]) {
$aReturn[$mKey] = $mValue;
}
}
} else {
$aReturn[$mKey] = $mValue;
}
}


return $aReturn;
}

What about my solution:

$diff = strcmp(serialize($arr1), serialize($arr2))

Came across this one while working on a platform upgrade for PHP 7.3 from 5.3 today... Since I'm storing this value for use later on, I wanted to be sure I didn't wind up with a 'partially' serialized array that may break things downstream.

Thanks kenorb for getting me onto the right path (I upvoted ur answer). The following is working nicely for me.

Code:

$a1 = [
'foo' => 'bar',
'bar' => [
'test' => 'test'
],
'foobar' => 'fizzbuzz'
];


$a2 = [
'foobar' => 'fizzbuzz',
'herp' => [
'derp' => 'herpderp'
]
];


$diff = array_diff(array_map('serialize', $a1), array_map('serialize', $a2));


$multidimensional_diff = array_map('unserialize', $diff);


print_r($multidimensional_diff);

Output:

Array
(
[foo] => bar
[bar] => Array
(
[test] => test
)


)

This is my solution for a similar problem. I want to compare two associative arrays and return the changed values, but some of the elements are arrays. So if I use

array_diff_assoc

, it gives me "Array to string error". My function will also compare the elements which are arrays and if there's a difference, it will return the array element. It is still a work in progress and not tested extensively. For example:

public static $example1 = [
'id' => 1,
'status' => 2,
'elements' => ['test', 'example'],
'different' => ['old' => 5, 'new' => 9]
];


public static $example2 = [
'id' => 1,
'status' => 3,
'elements' => ['test', 'example'],
'different' => ['old' => 5, 'new' => 8]
];


public static function test(){
die(var_dump(self::udiffAssoc(self::$example1, self::$example2)));
}


public static function udiffAssoc(array $array1, array $array2)
{
$checkDiff = function ($a, $b) use (&$checkDiff) {
if (is_array($a) && is_array($b)) {
return array_udiff_assoc($a, $b, $checkDiff);
} elseif (is_array($a)) {
return 1;
} elseif (is_array($b)) {
return -1;
} elseif ($a === $b) {
return 0;
} else {
return $a > $b ? 1 : -1;
}
};
return array_udiff_assoc($array1, $array2, $checkDiff);
}

if you run ::test it will return:

array(2) {
["status"]=>
int(2)
["different"]=>
array(2) {
["old"]=>
int(5)
["new"]=>
int(9)
}
}

We're getting cast to string warning because array_diff converts the elements to string before comparing... and we've passed ints, the key is to add a wrapper object since objects can be casted to string, then when comparison is done we're returning them back to ints as below

Or-

Your can just use a library https://github.com/voku/Arrayy#diffarray-array-static its have method called diff() can compare straight integer and other data types as well

For anyone who hits this, array_udiff() is a drop-in replacement that avoids the warning by using a callback instead of the internal string conversion. For example to diff [['a' => 1, 'b' => 0]] and [['a' => 1, 'b' => 1]]:

array_udiff([['a' => 1, 'b' => 0]], [['a' => 1, 'b' => 1]], function ($a, $b) { return $a != $b; });
// [["a" => 1,"b" => 0]]

Or with an arrow function if using php 7.4+:

array_udiff([['a' => 1, 'b' => 0]], [['a' => 1, 'b' => 1]], fn ($a, $b) => $a != $b);
// [["a" => 1,"b" => 0]]

Note that associative array comparison ignores key order, so ['a' => 1, 'b' => 0] == ['b' => 0, 'a' => 1]