Flatten multidimensional array of numbers recursively

Viewed 78

Let's say I have an array like this

Array
(
    [0] => 123
    [180] => Array
    (
        [400] => Array
        (
            [0] => 474
            [1] => 395
            [2] => 994
            [3] => 365
        )
    )
    [1] => 144
    [2] => 119
)

I would like to go though this array and generate a one dimensional array that contains all the numbers. In the loop, if we get an array, then the number is the corresponding array key.

I did something like this but it doesn't work:

function flatten_array($data) {
    $result = array();

    foreach ($data as $key => $value) {
        if(is_array($value)) {
            $result[] = $key;
            flatten_array($value);
        } else {
            $result[] = $value;
        }
    }

    return $result;
}
        

The resulting array I should get should be this one:

$result = Array
(
    [0] => 123
    [1] => 180
    [2] => 400
    [3] => 474
    [4] => 395
    [5] => 994
    [6] => 365
    [7] => 144
    [8] => 119
)      

Any help will be appreciated thanks.

4 Answers

Without using libraries, such result can be achieved this way:

function flattenArray(array $data) {
    $values = array_values($data);
    $result = [];

    foreach ($values as $value) {
        if (is_array($value)) {
            $result = array_merge(flattenArray($value), $result);
        } else {
            $result[] = $value;
        }
    }

    return $result;
}

P.S. Note the mistake in your original example:

$result[] = $key;
flatten_array($value);

You call the recursive function without using its returned result. And you assign the key instead.

Your code doesn't accumulate a result anywhere. You could return arrays and merge them, but to save allocation overhead I recommend using a single result array and passing it by reference into your recursive walk function. Here's a complete example:

function flatten_array($a) {
    function flatten($a, &$flat) {
        foreach ($a as $k => $v) {
            if (is_array($v)) {
                $flat[] = $k;
                flatten($v, $flat);
            }
            else {
                $flat[] = $v;
            }
        }
    }

    $flat = [];
    flatten($a, $flat);
    return $flat;
}

$a = [
    123,
    180 => [
        400 => [
            474,
            395,
            994,
            365,
        ]
    ],
    144,
    119
];

print_r(flatten_array($a));

If you expect your depth to be massive, you can do it iteratively to avoid an overflow. Note that this gives a breadth-first ordering:

function flatten_array($a) {
    $flat = [];

    for ($stack = [$a]; count($stack);) {
        foreach (array_pop($stack) as $k => &$v) {
            if (is_array($v)) {
                $flat[] = $k;
                $stack[] = $v;
            }
            else {
                $flat[] = $v;
            }
        }
    }

    return $flat;
}

You can use next recursive function:

function array_flat($arr) {
    $res = [];
    foreach($arr as $key=>$val) {
        if (is_array($val)) {
            // element is array: Push key to result and merge recursion result
            array_push($res, $key);
            $res = array_merge($res, array_flat($val));
        } else {
            // push simple value to result
            array_push($res, $val);
        }
        
    }
    return $res;
}

print_r(array_flat($arr));

Try working PHP code o PHPize.online

Related