Flatten Nested Arrays Using PHP
While browing the MooTools 1.2 source code, I found Array's flatten() method. The flatten() method takes nested arrays and "flattens" them all into one array. I asked myself how I could do that using PHP. The following is what I came up with.
The PHP
$myarray = array('a','b',array(array(array('x'),'y','z')),array(array('p')));
function array_flatten($array,$return)
{
for($x = 0; $x <= count($array); $x++)
{
if(is_array($array[$x]))
{
$return = array_flatten($array[$x],$return);
}
else
{
if($array[$x])
{
$return[] = $array[$x];
}
}
}
return $return;
}
$res = array_flatten($myarray,array());
The Result
Array
(
[0] => a
[1] => b
[2] => x
[3] => y
[4] => z
[5] => p
)
As you can see, array_flatten() is used recursively to sniff out values from the original array. While I don't believe I've ever found myself with an array as nested as my example, it's good to know that I can extract the values if necessary.
Comments
Be Heard!
Share your thoughts without being a jerk! And wrap your code in <code> tags, f00!
Here is another function without the need for recursion.
function array_flatten($array, $maintain_keys=false) {
$array = print_r($array, true);
preg_match_all(“/[(.+?)].*?\=>(.+?)\n/”, $array, $matches);
$array = array();
foreach ($matches[0] as $k=>$v) {
$key = trim($matches[1][$k]);
$value = trim($matches[2][$k]);
if (preg_match(“/^array$/i”, $value)) continue;
if (array_key_exists($key, $array) || !$maintain_keys) {
$array[] = $value;
} else $array[$key] = $value;
}
return $array;
}
$arr = array(‘alpha’=>’a',’b',array(array(array(‘beta’=>’x'),’alpha’=>’y',’z')),array(array(‘p’)));
echo ” . print_r($arr, true) . ”;
echo ” . print_r(array_flatten($arr) , true) . ”;
echo ” . print_r(array_flatten($arr, true) , true) . ”;
Wonder if the array keys will be the same when used with mysql_fetch_assoc
I went through this same issue with python a couple years ago[1]. Strangely, in all my years of using php, I never really needed flatten (well maybe a couple times it would have been handy). Python doesn’t handle recursion very well, so an iterative version is pretty much a necessity for it to be of any use. Here is a php translation of the python version I ended up using:
function array_flatten ($array)
{
$i = 0;
while ($i < count ($array))
{
while (is_array ($array[$i]))
{
if (!$array[$i])
{
array_splice ($array, $i, 1);
--$i;
break;
}
else
{
array_splice ($array, $i, 1, $array[$i]);
}
}
++$i;
}
return $array;
}
It’s much slower than your implementation (I assume because of the overhead of array_splice compared to python’s list.pop, since the python version performs comparably yours–it just doesn’t grok 2000 levels of recursion. In fact, when I use a translation of your php version, python dies very soon with “maximum recursion depth exceeded”).
Ps. One small note, on 5.2.6 I get warnings about trying to accessing invalid indexes from the $array[$x] on lines 7 and 13–prefixing them with “@” prevents them from incurring the slowdown of invoking the warning of course (just thought I’d mention it for posterity).
__
[1] http://rightfootin.blogspot.com/2006/09/more-on-python-flatten.html
Replace line #5 from
for($x = 0; $x <= count($array); $x++)
to
for($x = 0; $x < count($array); $x++)
(<= become <)
otherwise you get a notice “error” (Undefined offset) for every array fetched by the function.
I did a different approach, since the requirement was also different, and the outcome is boasted
about at php-trivandrum, as function array_flatten
Thanks for the post, this function is very handy.
Although, as we are retrieving a new array with new indexes it might be nice to have the same function that can flatten an associative array.
Here is your function modified to work with associative array:
function array_flatten($array, $return=array()) {
foreach ($array AS $key => $value) {
if(is_array($value))
{
$return = array_flatten($value,$return);
}
else
{
if($value)
{
$return[] = $value;
}
}
}
return $return;
}
So $myarray = array(‘a’,'b’,array(array(‘test’ => array(‘x’),’y',’nested2′ => ‘z’)),array(‘nested3′ => array(‘p’))) will also work as expected.
David! You just saved me hours!
@Manu: if you replace $return[] = $value; with $return[$key] = $value; you get the preserve the index names of the source array(s). Don’t know if that’s everyone preference, but that was what I was looking for. If one wanted to make this optional, he could add a 3rd optional parameter to this function to indicate wether or not to preserve index names, like so:
function array_flatten($array, $return=array(), $preserve_index_names = false) {
foreach ($array AS $key => $value) {
if(is_array($value)) {
$return = array_flatten($value,$return, $preserve_index_names);
}
else {
if($value) {
if($preserve_index_names === false) {
$return[] = $value;
}
else {
$return[$key] = $value;
}
}
}
}
return $return;
}
And then run it like so to get a flattened array back with index names preserved:
$result = array_flatten($input, array(), true);
And like this if you don’t want the index names preserved:
$result = array_flatten($input);
Great function, thanks David.
This is what i use, sposed to be faster than most methods:
function flattenArray(array $array){
$ret_array = array();
foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $value)
{
$ret_array[] = $value;
}
return $ret_array;
}
NOTE: this function will omit zero values. for example:
$test[0][0] = ’0′;
$test[0][1] = ‘b’;
$test[1][0] = ‘c’;
$test[1][1] = ‘d’;
array_flatten($test) will output:
Array ( [0] => b [1] => c [2] => d )
to fix this problem, on line 13 use:
if(isset($array[$x]))
instead of:
if($array[$x])
here’s the whole function with the line replaced:
function array_flatten($array,$return=array())
{
for($x = 0; $x <= count($array); $x++)
{
if(is_array($array[$x]))
{
$return = array_flatten($array[$x],$return);
}
else
{
if(isset($array[$x]))
{
$return[] = $array[$x];
}
}
}
return $return;
}
I know this post is a bit old, but here’s what I came up with – it’s super fast and concise.
function array_flatten( $array ) {$arr = array();
function flatten( $item, $key, $flattened ) {
$flattened[] = $item;
};
array_walk_recursive($array, 'flatten', &$arr);
return $arr;
}
Made a few improvements/simplifications, allowed associative arrays, etc.
function array_flatten(array $array, array $return = array()) {
foreach ($array as $k => $item) {
if (is_array($item))
$return = array_flatten($item, $return);
elseif ($item)
$return[] = $item;
}
return $return;
}
about the code on very top. their is an issue on using for loop, if the nested array have a key, it wont show the value. Instead use foreach loop. I have some revised on the code above, I hope it will help.
function array_flatten($array, $return)
{
foreach($array as $key => $val)
{
if(is_array($val))
{
$return = array_flatten($val, $return);
}
else
{
if($val)
{
$return[] = $val;
}
}
}
return $return;
}
$test= array(‘ddd’,
array(“hello” => ‘one’,'two’,array(array(‘inner’=>’innerval’, ‘innerval2′,array(‘key_inner’=>’inner_inner’)))),’ccc’,
array(‘a’,'b’),
array(‘dog’,'cat’)
);
$result = array_flatten($test,array());
print_r($result);
Output:
Array
(
[0] => ddd
[1] => one
[2] => two
[3] => innerval
[4] => innerval2
[5] => inner_inner
[6] => ccc
[7] => a
[8] => b
[9] => dog
[10] => cat
)