10

Let's say I want to do this:

$a = array_intersect_assoc(
 array(
  'key1' => array(
   'key2' => 'value2'
  ),
  'key3' => 'value3',
  'key4' => 'value4'
 ),

 array(
  'key1' => array(
   'key2' => 'some value not in the first parameter'
  ),
  'key3' => 'another value'
 )
);

var_dump( $a );

The printed result is:

array
  'key1' => 
    array
      'key2' => string 'value2' (length=6)

It's clear that values associated with 'key2' in both arrays are not the same, however array_intersect_assoc() still return 'key2' => 'value2' as the intersected value.

Is this the expected behavior of array_intersect_assoc()?

Thanks!

garyc40
  • 343
  • 1
  • 3
  • 8
  • 3
    That's pretty amazing! Especially since `array('key2' => 'value2') == array('key2' => 'some value not in the first parameter') === false`, whereas `array('key2' => 'value2') == array('key2' => 'value2') === true`. I think I speak for many people here when I say: huh. – Spiny Norman Jan 07 '11 at 15:35

4 Answers4

9

Yes, it's the expected behavior, because the comparison is done using string representations, and the function does not recurse down nested arrays. From the manual:

The two values from the key => value pairs are considered equal only if (string) $elem1 === (string) $elem2 . In other words a strict type check is executed so the string representation must be the same.

If you tried to intersect with an array with 'key1' => 'Array', you'd get the same result because the string representation of an array is always 'Array'.

One of the user-contributed notes, by nleippe, contains a recursive implementation that looks promising (I modified the third line to do string comparison on any non-array values):

function array_intersect_assoc_recursive(&$arr1, &$arr2) {
    if (!is_array($arr1) || !is_array($arr2)) {
//      return $arr1 == $arr2; // Original line
        return (string) $arr1 == (string) $arr2;
    }
    $commonkeys = array_intersect(array_keys($arr1), array_keys($arr2));
    $ret = array();
    foreach ($commonkeys as $key) {
        $ret[$key] =& array_intersect_assoc_recursive($arr1[$key], $arr2[$key]);
    }
    return $ret;
}
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • I've updated the latter implementation with a more mainstream backwards compatibility http://www.php.net/manual/en/function.array-intersect-assoc.php#111707. – Gajus Mar 19 '13 at 21:08
  • Above function worked for me, but I had to remove ampersand to prevent a strict warning in PHP 5.6.30 on Ubuntu 16.04 because of the use of the reference in the assignment in the foreach loop i.e. change `$ret[$key] =& array_intersect_assoc_recursive($arr1[$key], $arr2[$key]);` to `$ret[$key] = array_intersect_assoc_recursive($arr1[$key], $arr2[$key]);` – KolaB May 08 '17 at 11:01
  • 2
    the `$ret[$key] =& array_intersect_assoc_recursive...` causes warning `Only variables should be assigned by reference...`. what could be used instead of ampersand ? – T.Todua Feb 06 '20 at 12:40
  • @T.Todua See my answer to this question https://stackoverflow.com/questions/4627076/php-question-how-to-array-intersect-assoc-recursively/65825499#65825499 – Maxim Mandrik Jan 21 '21 at 10:32
  • 1
    @KolaB Your decision is wrong, because the function's response will only record Boolean comparison values, not the actual array values. See my answer to this question https://stackoverflow.com/questions/4627076/php-question-how-to-array-intersect-assoc-recursively/65825499#65825499 – Maxim Mandrik Jan 21 '21 at 10:34
1
function array_key_match_recursive(array $main, array $other, $i = 0, &$result = []) {
    foreach($main as $key => $value) {
        $k = sprintf('%s%s', str_repeat('=', $i), $key);
        if (!isset($other[$key])) {
            $result[$k][] = 'not key';
        }
        if (!is_array($value) && empty($other[$key])) {
            $result[$k][] = 'value empty';
        }
        if (is_array($value) && isset($other[$key])) {
            array_key_match_recursive($value, $other[$key], ++$i, $result);
        }
    }

    //return (bool) !$result;
    return $result;
}
bytsigan
  • 11
  • 2
1

A function that does what you need:

/**
 * Get array intersect assoc recursive.
 *
 * @param mixed $value1
 * @param mixed $value2
 *
 * @return array|bool
 */
function getArrayIntersectAssocRecursive(&$value1, &$value2)
{
    if (!is_array($value1) || !is_array($value1)) {
        return $value1 === $value2;
    }

    $intersectKeys = array_intersect(array_keys($value1), array_keys($value2));

    $intersectValues = [];
    foreach ($intersectKeys as $key) {
        if (getArrayIntersectAssocRecursive($value1[$key], $value2[$key])) {
            $intersectValues[$key] = $value1[$key];
        }
    }

    return $intersectValues;
}
Maxim Mandrik
  • 377
  • 1
  • 5
  • 11
-1

My version based on @BoltClock version:

function array_intersect_assoc_recursive($arr1, $arr2) {
    if (!is_array($arr1) || !is_array($arr2)) {
        return $arr1;
    }
    $commonkeys = array_keys($arr1);
    if (!array_key_exists('$', $arr2)){
        $commonkeys = array_intersect(array_keys($arr1), array_keys($arr2));
    }
    $ret = array();
    foreach ($commonkeys as $key) {
        $ret[$key] = array_intersect_assoc_recursive($arr1[$key], array_key_exists('$', $arr2) ? $arr2['$'] : $arr2[$key]);
    }
    return $ret;
}

I use this code to filter data inside complex array

example:

$filter = [
    'channels' => [
        '$' => [
            'id' => 1,
            'type' => 1,
            'count' => 1
        ]
    ],
    'user' => [
        'id' => 1,
        'type' => 1
    ]
];
$data = [
    'user' => [
        'id' => '1234',
        'type' => true,
        'counter' => 14,
    ],
    'filteredField' => 4,
    'channels' => [
        ['id' => '567', 'type' => 'other', 'count' => 1345, 'filteredField' => 5,],
        ['id' => '890', 'type' => 'other', 'count' => 5456, 'filteredField' => 7,],
    ],
];
print_r(array_intersect_assoc_recursive($data, $filter));

test online: https://onlinephp.io/c/3be04

Luca Camillo
  • 796
  • 9
  • 9
  • Your adaptation is not suitable for this question. In your sample data there are no associative matches between twe two arrays. It seems you are seeking recursive key-only intersections. You should have a look at @deceze's answer to [Filter 2D associative array using keys from multiple levels of another 2D associative array](https://stackoverflow.com/a/12173705/2943403) – mickmackusa Oct 29 '22 at 13:47