skip to Main Content

I have a multidimensional array with an indeterminant depth. I want to keep only some keys depending a collection of paths, but I need a generic, recursive function and not a hardcoded filter.

Example:

$originalArray = array(
    'key1' => array(
        'key1' => 'value1',
        'key2' => 'value2',
    ),
    'key2' => array(
        'key1' => 'value3',
        'key2' => 'value4',
        'key3' => array(
            'key1' => 'value5',
            'key2' => array(
                'key1' => 'value6',
                'key2' => 'value',
            ),
        ),
    ),
    'key3' => array(
        "key1" => "value1",
        "key2" => array(
            array(
                'key1' => 'value1',
                'key2' => 'value2',
                'key3' => 'value2',
            ),
            array(
                'key1' => 'value1',
                'key2' => 'value2',
                'key3' => 'value3',
            ),
        )
    ),
);

$pathsToRetain = array(
    array('key1', 'key1'),
    array('key2', 'key1'),
    array('key2', 'key3', 'key1'),
    array('key2', 'key3', 'key2', 'key1'),
    array("key3", "key2", "*", 'key1'),
    array("key3", "key2", "*", 'key3')
);

$this->filterArrayByPath($originalArray, pathsToRetain);

Result shoud be :

$result = array(
    'key1' => array(
        'key1' => 'value1',
    ),
    'key2' => array(
        'key1' => 'value3',
        'key3' => array(
            'key1' => 'value5',
            'key2' => array(
                'key1' => 'value6',
            ),
        ),
    ),
    'key3' => array(
        "key2" => array(
            array(
                'key1' => 'value1',
                'key3' => 'value2',
            ),
            array(
                'key1' => 'value1',
                'key3' => 'value3',
            ),
        )
    ),
);

One of the difficulties is on wildcard for arrays without key (key3->key2 contains arrays without keys and I want to apply the filter to both of them, or use 0, 1, 3 as key if not wildcard)

2

Answers


  1. This can be done using a recursive function.

    Give this ago:

    function filterArrayByPath($array, $pathsToRetain) {
        $filteredArray = [];
    
        foreach ($pathsToRetain as $path) {
            $currentArray = &$array;
            $validPath = true;
    
            foreach ($path as $key) {
                if ($key === '*') {
                    $currentArray = array_merge_recursive($currentArray, array_fill_keys(array_keys($currentArray), []));
                } elseif (is_array($currentArray) && array_key_exists($key, $currentArray)) {
                    $currentArray = &$currentArray[$key];
                } else {
                    $validPath = false;
                    break;
                }
            }
    
            if ($validPath) 
                $filteredArray = array_merge_recursive($filteredArray, $currentArray);
            
        }
    
        return $filteredArray;
    }
    
    Login or Signup to reply.
  2. I’ve fashioned a recursive script to remove disqualified elements while modifying the input array by reference. If you refer for the function to "return" the modified payload, you can return $array at the end; but just keep in mind that your original input array WILL be mutated by this function.

    The demo link has some additional commented checkpoints if you’d like to see the progress internally.

    The script will iterate the current level of the input array and check the first values of each row in the path array. If a qualifying key is found, then a flag is set to not unset the element. If the element qualifies and there are more paths to traverse, then the function is called again with reduced parameters.

    Code: (Demo)

    function filterArrayByPath(&$array, $pathsToRetain) {
        foreach ($array as $k => &$v) {
            $soFarSoGood = false;
            $paths = [];
            foreach ($pathsToRetain as $path) {
                $requiredKey = array_shift($path);
                if (in_array($requiredKey, ['*', $k])) {
                    $soFarSoGood = true;
                    if ($path) {
                        $paths[] = $path;
                    } else {
                        break;
                    }
                }
            }
            if (!$soFarSoGood) {
                unset($array[$k]);
            } elseif ($paths) {
                filterArrayByPath($v, $paths);
            }
        }
        //return $array;
    }
    filterArrayByPath($originalArray, $pathsToRetain);
    var_export($originalArray);
    // if returning the array: var_export(filterArrayByPath($originalArray, $pathsToRetain));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search