myol
myol

Reputation: 9838

Recursive function for checking multidimensional arrays

Really struggling with this. I have a multidimensional array n levels deep. Each 'array level' has information I need to check (category) and also check if it contains any arrays.

I want to return the category ids of all the arrays which have a category and do not contain an array (i.e. the leaves). I can echo output properly, but I am at a loss as how to return the ids in an array (without referencing)

I have tried RecursiveIteratorIterator::LEAVES_ONLY and RecursiveArrayIterator but I don't think they work in my case? (Maybe I am overlooking something)

$array

array(2) {
  ["1"]=>
  string(5) "stuff"
  ["2"]=>
  array(2) {
    ["category"]=>
    string(1) "0"
    ["1"]=>
    array(3) {
      [0]=>
      array(3) {
        ["category"]=>
        string(1) "1"
        ["1"]=>
        string(5) "stuff"
        ["2"]=>
        string(5) "stuff"
      }
      [1]=>
      array(5) {
        ["category"]=>
        string(1) "2"
        ["1"]=>
        string(5) "stuff"
        ["2"]=>
        string(5) "stuff" 
      }
      [1]=>
      array(5) {
        ["1"]=>
        string(5) "stuff"
        ["32"]=>
        string(5) "stuff" 
      }
    }
  }
}

My function

public function recurs($array, $cats = [])
{
    $array_cat = '';
    $has_array = false;

    // Check if an id exists in the array
    if (array_key_exists('category', $array)) {
        $array_cat = $array['category'];
    }

    // Check if an array exists within the array
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $has_array = true;
            $this->recurs($value, $cats);
        }
    }

    // If a leaf array
    if (!$has_array && is_numeric($array_cat)) {
        echo "echoing the category here works fine: " . $array_cat . "\n";

        return $array_cat;
    }
}

Calling it

$cats_array = $this->recurse($array)

Output echoed

echoing the category here works fine: 1
echoing the category here works fine: 2

How do I return the ids in an array to use in the $cats_array variable?

EDIT: The output should match the echo, so I should get an array containing (1, 2) since they are the only arrays with categories and no arrays within them

array(2){
    [0]=>
    int(1) "1"
    [1]=>
    int(1) "2"
}

Upvotes: 2

Views: 1271

Answers (1)

Ihor Burlachenko
Ihor Burlachenko

Reputation: 4905

If I understood you correctly this function will do the job:

function getCategories(array $data)
{
    if ($subArrays = array_filter($data, 'is_array')) {
        return array_reduce(array_map('getCategories', $subArrays), 'array_merge', array());
    };

    return array_key_exists('category', $data) ? array($data['category']) : array();
}

If the array contains any sub-arrays they will be returned by array_filter and you will enter the if statement. array_map will apply the function recursively to the sub-arrays and array_reduce will merge the results.

If the array doesn't contain any sub-arrays you will not enter the if statement and the function will return an array with the category if it is present.

Note that you might need to use array_unique to return unique categories.

Also for small performance optimization instead of array_key_exists you can use isset($array[$key]) || array_key_exists($key, $array).

Update

If you want to update your function to make it work you have to recursively collect and merge the sub results. In the following example I introduced a new variable for this:

public function recurs($array, $cats = [])
{
    $result = [];

    $array_cat = '';
    $has_array = false;

    // Check if an id exists in the array
    if (array_key_exists('category', $array)) {
        $array_cat = $array['category'];
    }

    // Check if an array exists within the array
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $has_array = true;
            $result = array_merge($result, $this->recurs($value, $cats));
        }
    }

    // If a leaf array
    if (!$has_array && is_numeric($array_cat)) {
        echo "echoing the category here works fine: " . $array_cat . "\n";

        return [$array_cat];
    }

    return $result;
}

Upvotes: 3

Related Questions