How to Flatten a Multidimensional Array?

    |
  • Added:
  • |
  • In: Basic PHP

Is it possible, in PHP, to flatten a (bi/multi)dimensional array without using recursion or references?

I'm only interested in the values so the keys can be ignored, I'm thinking in the lines of array_map() and array_values().

This Question Has 22 Answeres | Orginal Question | Alix Axel

You can do it with ouzo goodies:

 $result = Arrays::flatten($multidimensional); 

See: Here

Is it possible, in PHP, to flatten a (bi/multi)dimensional array without using recursion or references?

As of PHP 7, not that I know of. I created a solution I call array_moonwalk, which uses both references and recursion to flatten and deduplicate a multidimensional array and preserves the depth of the deepest node for each duplicate found for multidimensional array reconstruction.

Handles the deduplication of leaf nodes at different depths without using foreach, and the only answer so far to leverage array_walk. Not tested for performance. YMMV.

Because the code in here looks scary. Here is a function that will also convert a multidimensional array into html form compatible syntax, but which is easier to read.

/** * Flattens a multi demensional array into a one dimensional * to be compatible with hidden html fields. * * @param array $array * Array in the form: * array( * 'a' => array( * 'b' => '1' * ) * ) * * @return array * Array in the form: * array( * 'a[b]' => 1, * ) */ function flatten_array($array) { // Continue until $array is a one-dimensional array. $continue = TRUE; while ($continue) { $continue = FALSE; // Walk through top and second level of $array and move // all values in the second level up one level. foreach ($array as $key => $value) { if (is_array($value)) { // Second level found, therefore continue. $continue = TRUE; // Move each value a level up. foreach ($value as $child_key => $child_value) { $array[$key . '[' . $child_key . ']'] = $child_value; } // Remove second level array from top level. unset($array[$key]); } } } return $array; } 
<?php //recursive solution //test array $nested_array = [[1,2,[3]],4,[5],[[[6,[7=>[7,8,9,10]]]]]]; /*----------------------------------------- function call and return result to an array ------------------------------------------*/ $index_count = 1; $flatered_array = array(); $flatered_array = flat_array($nested_array, $index_count); /*----------------------------------------- Print Result -----------------------------------------*/ echo "<pre>"; print_r($flatered_array); /*----------------------------------------- function to flaten an array -----------------------------------------*/ function flat_array($nested_array, & $index_count, & $flatered_array) { foreach($nested_array AS $key=>$val) { if(is_array($val)) { flat_array($val, $index_count, $flatered_array); } else { $flatered_array[$index_count] = $val; ++$index_count; } } return $flatered_array; } ?> 

This is my solution, using a reference:

function arrayFlatten($array_in, &$array_out){ if(is_array($array_in)){ foreach ($array_in as $element){ arrayFlatten($element, $array_out); } } else{ $array_out[] = $array_in; } } $arr1 = array('1', '2', array(array(array('3'), '4', '5')), array(array('6'))); arrayFlatten($arr1, $arr2); echo "<pre>"; print_r($arr2); echo "</pre>"; 

If you have an array of objects and want to flatten it with a node, just use this function:

function objectArray_flatten($array,$childField) { $result = array(); foreach ($array as $node) { $result[] = $node; if(isset($node->$childField)) { $result = array_merge( $result, objectArray_flatten($node->$childField,$childField) ); unset($node->$childField); } } return $result; } 

I needed to represent PHP multidimensional array in HTML input format.

$test = [ 'a' => [ 'b' => [ 'c' => ['a', 'b'] ] ], 'b' => 'c', 'c' => [ 'd' => 'e' ] ]; $flatten = function ($input, $parent = []) use (&$flatten) { $return = []; foreach ($input as $k => $v) { if (is_array($v)) { $return = array_merge($return, $flatten($v, array_merge($parent, [$k]))); } else { if ($parent) { $key = implode('][', $parent) . '][' . $k . ']'; if (substr_count($key, ']') != substr_count($key, '[')) { $key = preg_replace('/\]/', '', $key, 1); } } else { $key = $k; } $return[$key] = $v; } } return $return; }; die(var_dump( $flatten($test) )); array(4) { ["a[b][c][0]"]=> string(1) "a" ["a[b][c][1]"]=> string(1) "b" ["b"]=> string(1) "c" ["c[d]"]=> string(1) "e" } 

Solution for 2 dimensional array

Please try this :

$array = your array $result = call_user_func_array('array_merge', $array); echo "<pre>"; print_r($result); 

EDIT : 21-Aug-13

Here is the solution which works for multi-dimensional array :

function array_flatten($array) { $return = array(); foreach ($array as $key => $value) { if (is_array($value)){ $return = array_merge($return, array_flatten($value)); } else { $return[$key] = $value; } } return $return; } $array = Your array $result = array_flatten($array); echo "<pre>"; print_r($result); 

Ref: http://php.net/manual/en/function.call-user-func-array.php

If you really don't like a recursion ... try shifting instead :)

$a = array(1,2,array(3,4, array(5,6,7), 8), 9); $o = []; for ($i=0; $i<count($a); $i++) { if (is_array($a[$i])) { array_splice($a, $i+1, 0, $a[$i]); } else { $o[] = $a[$i]; } } 

Note: In this simple version, this does not support array keys.

For php 5.2

function flatten(array $array) { $result = array(); if (is_array($array)) { foreach ($array as $k => $v) { if (is_array($v)) { $result = array_merge($result, flatten($v)); } else { $result[] = $v; } } } return $result; } 

You can use the Standard PHP Library (SPL) to "hide" the recursion.

$a = array(1,2,array(3,4, array(5,6,7), 8), 9); $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($a)); foreach($it as $v) { echo $v, " "; } 

prints

1 2 3 4 5 6 7 8 9 

Flattens two dimensional arrays only:

$arr = [1, 2, [3, 4]]; $arr = array_reduce($arr, function ($a, $b) { return array_merge($a, (array) $b); }, []); // Result: [1, 2, 3, 4] 

To flatten w/o recursion (as you have asked for), you can use a stack. Naturally you can put this into a function of it's own like array_flatten. The following is a version that works w/o keys:.

function array_flatten(array $array) { $flat = array(); // initialize return array $stack = array_values($array); // initialize stack while($stack) // process stack until done { $value = array_shift($stack); if (is_array($value)) // a value to further process { $stack = array_merge(array_values($value), $stack); } else // a value to take { $flat[] = $value; } } return $flat; } 

Elements are processed in their order. Because subelements will be moved on top of the stack, they will be processed next.

It's possible to take keys into account as well, however, you'll need a different strategy to handle the stack. That's needed because you need to deal with possible duplicate keys in the sub-arrays. A similar answer in a related question: PHP Walk through multidimensional array while preserving keys

I'm not specifically sure, but I I had tested this in the past: The RecurisiveIterator does use recursion, so it depends on what you really need. Should be possible to create a recursive iterator based on stacks as well:

foreach(new FlatRecursiveArrayIterator($array) as $key => $value) { echo "** ($key) $value\n"; } 

Demo

I didn't make it so far, to implement the stack based on RecursiveIterator which I think is a nice idea.

Uses recursion. Hopefully upon seeing how not-complex it is, your fear of recursion will dissipate once you see how not-complex it is.

function flatten($array) { if (!is_array($array)) { // nothing to do if it's not an array return array($array); } $result = array(); foreach ($array as $value) { // explode the sub-array, and add the parts $result = array_merge($result, flatten($value)); } return $result; } $arr = array('foo', array('nobody', 'expects', array('another', 'level'), 'the', 'Spanish', 'Inquisition'), 'bar'); echo '<ul>'; foreach (flatten($arr) as $value) { echo '<li>', $value, '</li>'; } echo '<ul>'; 

Output:

<ul><li>foo</li><li>nobody</li><li>expects</li><li>another</li><li>level</li><li>the</li><li>Spanish</li><li>Inquisition</li><li>bar</li><ul> 

Straightforward and One-liner answer.

function flatten_array(array $array) { return iterator_to_array( new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array))); } 

Usage:

$array = [ 'name' => 'Allen Linatoc', 'profile' => [ 'age' => 21, 'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ] ] ]; print_r( flatten_array($array) ); 

Output (in PsySH):

Array ( [name] => Allen Linatoc [age] => 21 [0] => Call of Duty [1] => Titanfall [2] => Far Cry ) 

Now it's pretty up to you now how you'll handle the keys. Cheers


EDIT (2017-03-01)

Quoting Nigel Alderton's concern/issue:

Just to clarify, this preserves keys (even numeric ones) so values that have the same key are lost. For example $array = ['a',['b','c']] becomes Array ([0] => b, [1] => c ). The 'a' is lost because 'b' also has a key of 0

Quoting Svish's answer:

Just add false as second parameter ($use_keys) to the iterator_to_array call

Try the following simple function:

function _flatten_array($arr) { while ($arr) { list($key, $value) = each($arr); is_array($value) ? $arr = $value : $out[$key] = $value; unset($arr[$key]); } return (array)$out; } 

So from this:

array ( 'und' => array ( 'profiles' => array ( 0 => array ( 'commerce_customer_address' => array ( 'und' => array ( 0 => array ( 'first_name' => 'First name', 'last_name' => 'Last name', 'thoroughfare' => 'Address 1', 'premise' => 'Address 2', 'locality' => 'Town/City', 'administrative_area' => 'County', 'postal_code' => 'Postcode', ), ), ), ), ), ), ) 

you get:

array ( 'first_name' => 'First name', 'last_name' => 'Last name', 'thoroughfare' => 'Address 1', 'premise' => 'Address 2', 'locality' => 'Town/City', 'administrative_area' => 'County', 'postal_code' => 'Postcode', ) 

This version can do deep, shallow, or a specific number of levels:

/** * @param array|object $array array of mixed values to flatten * @param int|boolean $level 0:deep, 1:shallow, 2:2 levels, 3... * @return array */ function flatten($array, $level = 0) { $level = (int) $level; $result = array(); foreach ($array as $i => $v) { if (0 <= $level && is_array($v)) { $v = flatten($v, $level > 1 ? $level - 1 : 0 - $level); $result = array_merge($result, $v); } elseif (is_int($i)) { $result[] = $v; } else { $result[$i] = $v; } } return $result; } 
/** * For merging values of a multidimensional array into one * * $array = [ * 0 => [ * 0 => 'a1', * 1 => 'b1', * 2 => 'c1', * 3 => 'd1' * ], * 1 => [ * 0 => 'a2', * 1 => 'b2', * 2 => 'c2', * ] * ]; * * becomes : * * $array = [ * 0 => 'a1', * 1 => 'b1', * 2 => 'c1', * 3 => 'd1', * 4 => 'a2', * 5 => 'b2', * 6 => 'c2', * * ] */ array_reduce ( $multiArray , function ($lastItem, $currentItem) { $lastItem = $lastItem ?: array(); return array_merge($lastItem, array_values($currentItem)); } ); 

Gist snippet

Just thought I'd point out that this is a fold, so array_reduce can be used:

array_reduce($my_array, 'array_merge', array()); 

EDIT: Note that this can be composed to flatten any number of levels. We can do this in several ways:

// Reduces one level $concat = function($x) { return array_reduce($x, 'array_merge', array()); }; // We can compose $concat with itself $n times, then apply it to $x // This can overflow the stack for large $n $compose = function($f, $g) { return function($x) use ($f, $g) { return $f($g($x)); }; }; $identity = function($x) { return $x; }; $flattenA = function($n) use ($compose, $identity, $concat) { return function($x) use ($compose, $identity, $concat, $n) { return ($n === 0)? $x : call_user_func(array_reduce(array_fill(0, $n, $concat), $compose, $identity), $x); }; }; // We can iteratively apply $concat to $x, $n times $uncurriedFlip = function($f) { return function($a, $b) use ($f) { return $f($b, $a); }; }; $iterate = function($f) use ($uncurriedFlip) { return function($n) use ($uncurriedFlip, $f) { return function($x) use ($uncurriedFlip, $f, $n) { return ($n === 0)? $x : array_reduce(array_fill(0, $n, $f), $uncurriedFlip('call_user_func'), $x); }; }; }; $flattenB = $iterate($concat); // Example usage: $apply = function($f, $x) { return $f($x); }; $curriedFlip = function($f) { return function($a) use ($f) { return function($b) use ($f, $a) { return $f($b, $a); }; }; }; var_dump( array_map( call_user_func($curriedFlip($apply), array(array(array('A', 'B', 'C'), array('D')), array(array(), array('E')))), array($flattenA(2), $flattenB(2)))); 

Of course, we could also use loops but the question asks for a combinator function along the lines of array_map or array_values.

This solution is non-recursive. Note that the order of the elements will be somewhat mixed.

function flatten($array) { $return = array(); while(count($array)) { $value = array_shift($array); if(is_array($value)) foreach($value as $sub) $array[] = $sub; else $return[] = $value; } return $return; } 

As of PHP 5.3 the shortest solution seems to be array_walk_recursive() with the new closures syntax:

function flatten(array $array) { $return = array(); array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; }); return $return; } 

The trick is passing the both the source and destination arrays by reference.

function flatten_array(&$arr, &$dst) { if(!isset($dst) || !is_array($dst)) { $dst = array(); } if(!is_array($arr)) { $dst[] = $arr; } else { foreach($arr as &$subject) { flatten_array($subject, $dst); } } } $recursive = array('1', array('2','3',array('4',array('5','6')),'7',array(array(array('8'),'9'),'10'))); echo "Recursive: \r\n"; print_r($recursive); $flat = null; flatten_array($recursive, $flat); echo "Flat: \r\n"; print_r($flat); // If you change line 3 to $dst[] = &$arr; , you won't waste memory, // since all you're doing is copying references, and imploding the array // into a string will be both memory efficient and fast:) echo "String:\r\n"; echo implode(',',$flat); 

Search
I am...

Sajjad Hossain

I have five years of experience in web development sector. I love to do amazing projects and share my knowledge with all.

NEED HOSTING?
Are you searching for Good Quality hosting?
You can Try It!
Connect Social With PHPAns
Top