views:

133

answers:

5

I want to be able to know the level of an array as it is being built.

The code loops through a bunch of directories to create a massive multi-dimensional array.

As the array is being created, I want to know how deep in the array I am.

1    2    3    4
---------------------
Root
     A
          A2
          A3
          A4
               A4a
     B
          B2
          B3
     C
     D
     E
          E2
               E2a
          E3

In the example above, the root directory is in level 1. All single uppercase letters are in level 2. All uppercase letters with a number are in level 3. All uppercase letters with a number and a lowercase letter are in level 4.

As I'm building the array, is there any way for me to know which level I'm in? The array is being created with a recursive function.

This is a PHP question.

+3  A: 

One quick an easy answer is to simply add a "depth" parameter to your function and increment it when the function calls itself.

Basiclife
+1 Using recursion to work with the product of recursion — I like the sound of that.
BoltClock
-1 I don't get how this counts the dimension. In my eyes it covers only the case when the subdimension is in the first array element.
nikic
@nikic Apologies - I missed a [0] in the code...
Basiclife
Even so, an array could contain a string on the first slot, and an array on the second.
Colin Hebert
@Colin - Quite true. I now understan the original issue. I'll stick with just the depth argument as Sarfraz's solution is equivalent to what I'm trying to do :)
Basiclife
I was able to implement your function with success, but was still not satisfied with the result, so I went back to the idea of adding a depth counter, which is what I was trying to do in the first place. The only reason that this didn't work in the beginning was because I forgot to deincrement the depth counter in the recursive function. I ultimately decided to abstract the counter to a class, and that solved my problem. Thanks!
Bryan Downing
You're welcome :)
Basiclife
+2  A: 

This should do:

function array_depth($array) {
    $max_depth = 1;

    foreach ($array as $value) {
        if (is_array($value)) {
            $depth = array_depth($value) + 1;

            if ($depth > $max_depth) {
                $max_depth = $depth;
            }
        }
    }

    return $max_depth;
}
Sarfraz
A: 
function calc_dimensions(array $array) {
    $dimensions = 1;
    $max = 0;
    foreach ($array as $value) {
        if (is_array($value)) {
            $subDimensions = calc_dimensions($value);
            if ($subDimensions > $max) {
                $max = $subDimensions;
            }
        }
    }

    return $dimensions+$max;
}

$array = array(
    array(
        array(
            4 => 5,
            array(
                6 => 6
            )
        )
    ),
    1 => 5
);

echo calc_dimensions($array)."\n";
bobrik
Why are you using both `$max` and `$dimensions`. One of them would suffice.
nikic
yep, of course :)
bobrik
A: 

Maybe you are asking the wrong question. What is the final goal? For example there is a RecursiveDirectoryIterator class within SPL, maybe that will do for you? Building a big multidimensional array will eat loads of memory, so maybe simply iterating over all those files recursively would suffice?

nikic
Thanks for the comment. The directory structure and contents rarely changes, and when it does, we issue a new release. So I'm caching the array after it is created. Otherwise, yeah, we'd be screwed.
Bryan Downing
+4  A: 

This might be tangential to your question about the array, but you could kill two birds with one stone by using a recursive directory iterator.

$path_to_root = __DIR__;
$directories  = new ParentIterator(new RecursiveDirectoryIterator($path_to_root, RecursiveDirectoryIterator::CURRENT_AS_SELF));
$iterator     = new RecursiveIteratorIterator($directories, RecursiveIteratorIterator::SELF_FIRST);

foreach ($iterator as $item)
{
    printf("%d %s\n", $iterator->getDepth() + 1, $item->getSubPathname());
}

Which would output something like:

1 Root
2 Root/A
3 Root/A/A2
3 Root/A/A3
3 Root/A/A4
4 Root/A/A4/A4a
2 Root/B
3 Root/B/B2
3 Root/B/B3
2 Root/C
2 Root/D
2 Root/E
3 Root/E/E2
4 Root/E/E2/E2a
3 Root/E/E3

As you can see RecursiveIteratorIterator::getDepth() is used to get the current depth of the recursive iterator, which is the reason for suggesting this approach.


Alternative (if you must use an array)

Assuming your array structure looks something like:

$dirs = array(
    'Root' => array(
        'A' => array(
            'A2' => array(),
            'A3' => array(),
            'A4' => array(
                'A4a' => array(),
            ),
        ),
        'B' => array(
            'B2' => array(),
            'B3' => array(),
        ),
        'C' => array(),
        'D' => array(),
        'E' => array(
            'E2' => array(
                'E2a' => array(),
            ),
            'E3' => array(),
        ),
    ),
);

Then a very similar approach to getting the values from a recursive directory iterator (but this time with a recursive array iterator) can be used. A quick loop over the "parent" arrays can give us the "path" from the current item back to the root.

$recursive = new ParentIterator(new RecursiveArrayiterator($dirs));
$iterator  = new RecursiveIteratorIterator($recursive, RecursiveIteratorIterator::SELF_FIRST);

foreach ($iterator as $item)
{
    // Build path from "parent" array keys
    for ($path = "", $i = 0; $i <= $iterator->getDepth(); $i++) {
        $path .= "/" . $iterator->getSubIterator($i)->key();
    }
    // Output depth and "path"
    printf("%d %s\n", $iterator->getDepth() + 1, ltrim($path, "/"));
}

The output would be the same as the earlier one for the directory iterator.

TL;DR We can use recursive iterators from the SPL iterators to make working with recursive/deep structures much simpler.

TL;DR;TL;DR SPL, heck yeah!

salathe