phparraysmultidimensional-arrayfilterarray-intersect

Filter a multidimensional array (containing elements of irregular depth) using a whitelist of string values


I have a multidimensional array that I am searching through for specific values. If those values are found I need to retain those elements in a new array.

array_intersect() worked fine on PHP5.3, now on 5.4 it complains:

Notice: Array to string conversion.

I found that array_intersect() has an issue with multidimensional arrays on PHP5.4. https://bugs.php.net/bug.php?id=60198

This is the $options array I am searching through:

Array (
    [index1] => html
    [index2] => html
    [index3] => slide
    [index4] => tab
    [index5] => Array ( [0] => 123 )
)

Code that works on PHP5.3.x:

$lookfor   = array('slide', 'tab');
$found     = array_intersect($options, $lookfor);
print_r($found);

Array
(
    [index3] => slide
    [index4] => tab
)

but in 5.4.x, this throws the error mentioned above.

What would be another way to do this without a loop and without using error suppression.


Solution

  • array_intersect() isn't recursive. The function assumes the array is just one level deep and expects all the array elements to be scalars. When it finds a non-scalar value, i.e. a sub-array, it throws a Notice.

    This is vaguely mentioned in the documentation for array_intersect():

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

    One solution I could think of is to use array_filter():

    $lookfor = array('html', 'slide');
    $found   = array_filter($options, function($item) use ($lookfor) {
        return in_array($item, $lookfor);
    });
    

    Note: This still performs a looping and isn't any better than a simple foreach. In fact, it might be slower than a foreach if the array is large. I have no idea why you're trying to avoid loops — I personally think it'd be more cleaner if you just use a loop.

    Demo

    Another solution I could think of is to remove the sub-arrays before using array_intersect():

    <?php
    
    $options = array(
        'index1' => 'html',
        'index2' => 'html',
        'index3' => 'slide',
        'index4' => 'tab',
        'index5' => array(123),
    );
    
    $lookfor = array('html', 'slide');
    $scalars = array_filter($options,function ($item) { return !is_array($item); });
    $found = array_intersect ($scalars, $lookfor);
    
    print_r($found);
    

    Demo