tags:

views:

59

answers:

3

I have a function that has to accept an array of points, or an array of array of points (a 2 or 3 dimensional array). I'm looking for an reliable way to detect whether it has 2 or 3 levels. The thing is, I cannot count on the keys of the arrays to do the checking, so this wont work:

$levels = isset($array[0][0]) && is_array($array[0][0]) ? 3 : 2;

..as the first key might not be 0. It usually is, but I don't want to rely on this. And anyways, it's an crappy and a close minded way to do that. Optimally, I would like to check for any number of levels without having to loop through the whole array.

Here's what the arrays might look like:

array(5) {
    [2] => array(2) {
        [x] => 3
        [y] => 6
    }
    [3] => array(2) {
        [x] => 4
        [y] => 8
    }
    ...

And a three dimensional array would contain these arrays.

Some notes:

  • The arrays are large, so completely looping through the arrays is not a very good option
  • The arrays are numerically and sequentially indexed (with the exception of the last level, which has x and y)
  • The array keys might or might not start from 0

While writing this, I came up with a solution that might be feasible; a recursive function that checks the first item of an array, if it is, then call itself on the newly found array etc.

Are there any better, cleaner ideas? Bonus points for supporting arrays that might have both scalar values and arrays (eg. the first item of an array might be a string, but the next is an array).

+2  A: 

I don't see how you could do this without at least iterating through the array. The simple fact is that any one of the elements in your array could have an additional level. As a result, every element needs to be tested.

That being said, you can still use recursion to improve your code a bit:

/** 
 * Determine the maximum depth of an array.
 * @param $input The object to test. Might be an array, or might be an int (or
 *        any other object).
 * @param $startDepth The starting depth. Will be added as an offset to the
 *        result.
 * @return The depth of the array, plus any initial offset specified in
 *         $startDepth.
 */
function testDepth($input, $startDepth = 0) {
    if (is_array($input) {
        $max = $startDepth;
        for ($array as $i) {
            // Check what the depth of the given element is
            $result = testDepth($i, $startDepth + 1);
            // We only care about the maximum value
            if ($result > $max) {
                $max = $result;
            }
        }
        return $max;
    } else {
        // This isn't an array, so it's assumed not to be a container.
        // This doesn't add any depth to the parent array, so just return $startDepth
        return $startDepth;
    }
}

testDepth($array);
Trevor Johns
+3  A: 

If you expect a complete array or a complete array of arrays then you could try:-

if (isset $points[0][0][0])

If however your array is sparse its more difficult. The basic problem is that a php "array" is actually a one dimensional hash. The trick being that a value can be another "array". So you need to access to second level to determine whether its a value or an array.

Again if you expect a given array to contain only point values, or only other arrays you only need to check one entry so:

if ( is_array(current(current($points))) )

Should get you what you want: the current() function returns the current array pointer (which defaults to the first - so it will always be set to something), so the inside current($points) would get you $points[0] or the first entry with an actual value, likwise the outside current will get you something like $points[0][0].

James Anderson
The current(current($points)) solution works great for this case, so I'll use that. At least it's cleaner than anything I've figured out and it works also on arrays with unknown starting keys. Thanks!
Tatu Ulmanen
+1  A: 

$levels = is_array(current(current($array))) ? 3 : 2;

Wizzard