How to Flatten a Multidimensional Array? - 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().

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;
}

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

In PHP 5.6 and above you can flatten two dimensional arrays with array_merge after unpacking the outer array with ... operator. The code is simple and clear.
array_merge(...$a);
This works with collection of associative arrays too.
$a = [[10, 20], [30, 40]];
$b = [["x" => "A", "y" => "B"], ["y" => "C", "z" => "D"]];
print_r(array_merge(...$a));
print_r(array_merge(...$b));
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
)
Array
(
[x] => A
[y] => C
[z] => D
)
In PHP 8.0 and below, array unpacking does not work when the outer array has non numeric keys. Support for unpacking array with string keys is available from PHP 8.1. To support 8.0 and below, you should call array_values first.
$c = ["a" => ["x" => "A", "y" => "B"], "b" => ["y" => "C", "z" => "D"]];
print_r(array_merge(...array_values($c)));
Array
(
[x] => A
[y] => C
[z] => D
)
Update: Based on comment by #MohamedGharib (for PHP 7.3.x and older ref)
This will throw an error if the outer array is empty, since array_merge would be called with zero arguments. It can be be avoided by adding an empty array as the first argument.
array_merge([], ...$a);

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

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
{
array_unshift($stack, ...$value);
}
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.

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.

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

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>

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]

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;
}

I believe this is the cleanest solution without using any mutations nor unfamiliar classes.
<?php
function flatten($array)
{
return array_reduce($array, function($acc, $item){
return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
}, []);
}
// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));

The Laravel helper for flattening arrays is Arr::flatten()

From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.
$flatArr = array_merge(...$originalArray);

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',
)

You can do it with ouzo goodies:
$result = Arrays::flatten($multidimensional);
See: Here

If you want to keep also your keys that is solution.
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Unfortunately it outputs only final nested arrays, without middle keys. So for the following example:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output is:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)

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);

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.

How about using a recursive generator? https://ideone.com/d0TXCg
<?php
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
foreach (iterate($array) as $item) {
var_dump($item);
};
function iterate($array)
{
foreach ($array as $item) {
if (is_array($item)) {
yield from iterate($item);
} else {
yield $item;
}
}
}

/**
* 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

Anyone looking for a really clean solution to this; here's an option:
Taking an array of arrays with various key value configurations:
$test_array = array(
array('test' => 0, 0, 0, 0),
array(0, 0, 'merp' => array('herp' => 'derp'), 0),
array(0, 0, 0, 0),
array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ;
This will take only the values from each array and return a single flat array.
Output of values in result:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0

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;
}

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;
}

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;
}

If you want to keep intermediate keys:
function flattenArray(array &$result, $value, string $key = "")
{
if (!is_array($value)) {
$result[$key] = $value;
return $result;
}
foreach ($value as $subKey => $subArray) {
$newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
flattenArray($result, $subArray, $newKey);
}
return $result;
}
$nestedArray = [
"name" => "John",
"pets" => [
["id" => 1, "name" => "snooop"],
["id" => 2, "name" => "medor"],
],
"job" => ["title" => "developper"],
];
$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);
This will output:
array(6) {
["name"]=>
string(4) "John"
["pets_0_id"]=>
int(1)
["pets_0_name"]=>
string(6) "snooop"
["pets_1_id"]=>
int(2)
["pets_1_name"]=>
string(5) "medor"
["job_title"]=>
string(10) "developper"
}
See https://ideone.com/KXLtzZ#stdout

Non recursive, non references based implementation, as asked, which may be easier to understand than a recursion based implemetation.
Can manage arbitrary deep multidimensional arrays, can't flatten associative arrays.
It works by flattening the array one level per cycle, until it is completly valid.
function array_flatten(): array{
$result = func_get_args();
// check all elements of $list are not arrays
$_is_flat = function (array $list): bool {
foreach ($list as $val) {
if (is_array($val)) {
return false;
}
}
return true;
};
do {
$tmp = [];
foreach ($result as $val) {
if (is_array($val)) {
if (!array_is_list($val)) {
throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
}
$tmp = array_merge($tmp, $val);
} else {
$tmp[] = $val;
}
}
$result = $tmp;
} while (!$_is_flat($result));
return $result;
}
This are the cases it handles:
assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');

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"
}

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;
}

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>";

<?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;
}
?>

Related

I have an nested array and I want to transform it in single array. I don't know how to do it [duplicate]

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().
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;
}
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
In PHP 5.6 and above you can flatten two dimensional arrays with array_merge after unpacking the outer array with ... operator. The code is simple and clear.
array_merge(...$a);
This works with collection of associative arrays too.
$a = [[10, 20], [30, 40]];
$b = [["x" => "A", "y" => "B"], ["y" => "C", "z" => "D"]];
print_r(array_merge(...$a));
print_r(array_merge(...$b));
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
)
Array
(
[x] => A
[y] => C
[z] => D
)
In PHP 8.0 and below, array unpacking does not work when the outer array has non numeric keys. Support for unpacking array with string keys is available from PHP 8.1. To support 8.0 and below, you should call array_values first.
$c = ["a" => ["x" => "A", "y" => "B"], "b" => ["y" => "C", "z" => "D"]];
print_r(array_merge(...array_values($c)));
Array
(
[x] => A
[y] => C
[z] => D
)
Update: Based on comment by #MohamedGharib (for PHP 7.3.x and older ref)
This will throw an error if the outer array is empty, since array_merge would be called with zero arguments. It can be be avoided by adding an empty array as the first argument.
array_merge([], ...$a);
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
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
{
array_unshift($stack, ...$value);
}
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.
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.
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
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>
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]
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;
}
I believe this is the cleanest solution without using any mutations nor unfamiliar classes.
<?php
function flatten($array)
{
return array_reduce($array, function($acc, $item){
return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
}, []);
}
// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));
The Laravel helper for flattening arrays is Arr::flatten()
From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.
$flatArr = array_merge(...$originalArray);
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',
)
You can do it with ouzo goodies:
$result = Arrays::flatten($multidimensional);
See: Here
If you want to keep also your keys that is solution.
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Unfortunately it outputs only final nested arrays, without middle keys. So for the following example:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output is:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
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);
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.
How about using a recursive generator? https://ideone.com/d0TXCg
<?php
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
foreach (iterate($array) as $item) {
var_dump($item);
};
function iterate($array)
{
foreach ($array as $item) {
if (is_array($item)) {
yield from iterate($item);
} else {
yield $item;
}
}
}
/**
* 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
Anyone looking for a really clean solution to this; here's an option:
Taking an array of arrays with various key value configurations:
$test_array = array(
array('test' => 0, 0, 0, 0),
array(0, 0, 'merp' => array('herp' => 'derp'), 0),
array(0, 0, 0, 0),
array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ;
This will take only the values from each array and return a single flat array.
Output of values in result:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
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;
}
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;
}
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;
}
If you want to keep intermediate keys:
function flattenArray(array &$result, $value, string $key = "")
{
if (!is_array($value)) {
$result[$key] = $value;
return $result;
}
foreach ($value as $subKey => $subArray) {
$newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
flattenArray($result, $subArray, $newKey);
}
return $result;
}
$nestedArray = [
"name" => "John",
"pets" => [
["id" => 1, "name" => "snooop"],
["id" => 2, "name" => "medor"],
],
"job" => ["title" => "developper"],
];
$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);
This will output:
array(6) {
["name"]=>
string(4) "John"
["pets_0_id"]=>
int(1)
["pets_0_name"]=>
string(6) "snooop"
["pets_1_id"]=>
int(2)
["pets_1_name"]=>
string(5) "medor"
["job_title"]=>
string(10) "developper"
}
See https://ideone.com/KXLtzZ#stdout
Non recursive, non references based implementation, as asked, which may be easier to understand than a recursion based implemetation.
Can manage arbitrary deep multidimensional arrays, can't flatten associative arrays.
It works by flattening the array one level per cycle, until it is completly valid.
function array_flatten(): array{
$result = func_get_args();
// check all elements of $list are not arrays
$_is_flat = function (array $list): bool {
foreach ($list as $val) {
if (is_array($val)) {
return false;
}
}
return true;
};
do {
$tmp = [];
foreach ($result as $val) {
if (is_array($val)) {
if (!array_is_list($val)) {
throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
}
$tmp = array_merge($tmp, $val);
} else {
$tmp[] = $val;
}
}
$result = $tmp;
} while (!$_is_flat($result));
return $result;
}
This are the cases it handles:
assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');
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"
}
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;
}
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>";
<?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;
}
?>

How can i merge array inside multidimensional array [duplicate]

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().
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;
}
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
In PHP 5.6 and above you can flatten two dimensional arrays with array_merge after unpacking the outer array with ... operator. The code is simple and clear.
array_merge(...$a);
This works with collection of associative arrays too.
$a = [[10, 20], [30, 40]];
$b = [["x" => "A", "y" => "B"], ["y" => "C", "z" => "D"]];
print_r(array_merge(...$a));
print_r(array_merge(...$b));
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
)
Array
(
[x] => A
[y] => C
[z] => D
)
In PHP 8.0 and below, array unpacking does not work when the outer array has non numeric keys. Support for unpacking array with string keys is available from PHP 8.1. To support 8.0 and below, you should call array_values first.
$c = ["a" => ["x" => "A", "y" => "B"], "b" => ["y" => "C", "z" => "D"]];
print_r(array_merge(...array_values($c)));
Array
(
[x] => A
[y] => C
[z] => D
)
Update: Based on comment by #MohamedGharib (for PHP 7.3.x and older ref)
This will throw an error if the outer array is empty, since array_merge would be called with zero arguments. It can be be avoided by adding an empty array as the first argument.
array_merge([], ...$a);
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
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
{
array_unshift($stack, ...$value);
}
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.
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.
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
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>
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]
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;
}
I believe this is the cleanest solution without using any mutations nor unfamiliar classes.
<?php
function flatten($array)
{
return array_reduce($array, function($acc, $item){
return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
}, []);
}
// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));
The Laravel helper for flattening arrays is Arr::flatten()
From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.
$flatArr = array_merge(...$originalArray);
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',
)
You can do it with ouzo goodies:
$result = Arrays::flatten($multidimensional);
See: Here
If you want to keep also your keys that is solution.
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Unfortunately it outputs only final nested arrays, without middle keys. So for the following example:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output is:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
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);
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.
How about using a recursive generator? https://ideone.com/d0TXCg
<?php
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
foreach (iterate($array) as $item) {
var_dump($item);
};
function iterate($array)
{
foreach ($array as $item) {
if (is_array($item)) {
yield from iterate($item);
} else {
yield $item;
}
}
}
/**
* 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
Anyone looking for a really clean solution to this; here's an option:
Taking an array of arrays with various key value configurations:
$test_array = array(
array('test' => 0, 0, 0, 0),
array(0, 0, 'merp' => array('herp' => 'derp'), 0),
array(0, 0, 0, 0),
array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ;
This will take only the values from each array and return a single flat array.
Output of values in result:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
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;
}
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;
}
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;
}
If you want to keep intermediate keys:
function flattenArray(array &$result, $value, string $key = "")
{
if (!is_array($value)) {
$result[$key] = $value;
return $result;
}
foreach ($value as $subKey => $subArray) {
$newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
flattenArray($result, $subArray, $newKey);
}
return $result;
}
$nestedArray = [
"name" => "John",
"pets" => [
["id" => 1, "name" => "snooop"],
["id" => 2, "name" => "medor"],
],
"job" => ["title" => "developper"],
];
$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);
This will output:
array(6) {
["name"]=>
string(4) "John"
["pets_0_id"]=>
int(1)
["pets_0_name"]=>
string(6) "snooop"
["pets_1_id"]=>
int(2)
["pets_1_name"]=>
string(5) "medor"
["job_title"]=>
string(10) "developper"
}
See https://ideone.com/KXLtzZ#stdout
Non recursive, non references based implementation, as asked, which may be easier to understand than a recursion based implemetation.
Can manage arbitrary deep multidimensional arrays, can't flatten associative arrays.
It works by flattening the array one level per cycle, until it is completly valid.
function array_flatten(): array{
$result = func_get_args();
// check all elements of $list are not arrays
$_is_flat = function (array $list): bool {
foreach ($list as $val) {
if (is_array($val)) {
return false;
}
}
return true;
};
do {
$tmp = [];
foreach ($result as $val) {
if (is_array($val)) {
if (!array_is_list($val)) {
throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
}
$tmp = array_merge($tmp, $val);
} else {
$tmp[] = $val;
}
}
$result = $tmp;
} while (!$_is_flat($result));
return $result;
}
This are the cases it handles:
assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');
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"
}
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;
}
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>";
<?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;
}
?>

PHP merge multi dimensional array [duplicate]

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().
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;
}
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
In PHP 5.6 and above you can flatten two dimensional arrays with array_merge after unpacking the outer array with ... operator. The code is simple and clear.
array_merge(...$a);
This works with collection of associative arrays too.
$a = [[10, 20], [30, 40]];
$b = [["x" => "A", "y" => "B"], ["y" => "C", "z" => "D"]];
print_r(array_merge(...$a));
print_r(array_merge(...$b));
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
)
Array
(
[x] => A
[y] => C
[z] => D
)
In PHP 8.0 and below, array unpacking does not work when the outer array has non numeric keys. Support for unpacking array with string keys is available from PHP 8.1. To support 8.0 and below, you should call array_values first.
$c = ["a" => ["x" => "A", "y" => "B"], "b" => ["y" => "C", "z" => "D"]];
print_r(array_merge(...array_values($c)));
Array
(
[x] => A
[y] => C
[z] => D
)
Update: Based on comment by #MohamedGharib (for PHP 7.3.x and older ref)
This will throw an error if the outer array is empty, since array_merge would be called with zero arguments. It can be be avoided by adding an empty array as the first argument.
array_merge([], ...$a);
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
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
{
array_unshift($stack, ...$value);
}
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.
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.
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
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>
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]
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;
}
I believe this is the cleanest solution without using any mutations nor unfamiliar classes.
<?php
function flatten($array)
{
return array_reduce($array, function($acc, $item){
return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
}, []);
}
// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));
The Laravel helper for flattening arrays is Arr::flatten()
From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.
$flatArr = array_merge(...$originalArray);
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',
)
You can do it with ouzo goodies:
$result = Arrays::flatten($multidimensional);
See: Here
If you want to keep also your keys that is solution.
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Unfortunately it outputs only final nested arrays, without middle keys. So for the following example:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output is:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
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);
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.
How about using a recursive generator? https://ideone.com/d0TXCg
<?php
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
foreach (iterate($array) as $item) {
var_dump($item);
};
function iterate($array)
{
foreach ($array as $item) {
if (is_array($item)) {
yield from iterate($item);
} else {
yield $item;
}
}
}
/**
* 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
Anyone looking for a really clean solution to this; here's an option:
Taking an array of arrays with various key value configurations:
$test_array = array(
array('test' => 0, 0, 0, 0),
array(0, 0, 'merp' => array('herp' => 'derp'), 0),
array(0, 0, 0, 0),
array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ;
This will take only the values from each array and return a single flat array.
Output of values in result:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
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;
}
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;
}
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;
}
If you want to keep intermediate keys:
function flattenArray(array &$result, $value, string $key = "")
{
if (!is_array($value)) {
$result[$key] = $value;
return $result;
}
foreach ($value as $subKey => $subArray) {
$newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
flattenArray($result, $subArray, $newKey);
}
return $result;
}
$nestedArray = [
"name" => "John",
"pets" => [
["id" => 1, "name" => "snooop"],
["id" => 2, "name" => "medor"],
],
"job" => ["title" => "developper"],
];
$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);
This will output:
array(6) {
["name"]=>
string(4) "John"
["pets_0_id"]=>
int(1)
["pets_0_name"]=>
string(6) "snooop"
["pets_1_id"]=>
int(2)
["pets_1_name"]=>
string(5) "medor"
["job_title"]=>
string(10) "developper"
}
See https://ideone.com/KXLtzZ#stdout
Non recursive, non references based implementation, as asked, which may be easier to understand than a recursion based implemetation.
Can manage arbitrary deep multidimensional arrays, can't flatten associative arrays.
It works by flattening the array one level per cycle, until it is completly valid.
function array_flatten(): array{
$result = func_get_args();
// check all elements of $list are not arrays
$_is_flat = function (array $list): bool {
foreach ($list as $val) {
if (is_array($val)) {
return false;
}
}
return true;
};
do {
$tmp = [];
foreach ($result as $val) {
if (is_array($val)) {
if (!array_is_list($val)) {
throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
}
$tmp = array_merge($tmp, $val);
} else {
$tmp[] = $val;
}
}
$result = $tmp;
} while (!$_is_flat($result));
return $result;
}
This are the cases it handles:
assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');
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"
}
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;
}
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>";
<?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;
}
?>

PHP merge array of arrays into one array [duplicate]

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().
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;
}
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
In PHP 5.6 and above you can flatten two dimensional arrays with array_merge after unpacking the outer array with ... operator. The code is simple and clear.
array_merge(...$a);
This works with collection of associative arrays too.
$a = [[10, 20], [30, 40]];
$b = [["x" => "A", "y" => "B"], ["y" => "C", "z" => "D"]];
print_r(array_merge(...$a));
print_r(array_merge(...$b));
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
)
Array
(
[x] => A
[y] => C
[z] => D
)
In PHP 8.0 and below, array unpacking does not work when the outer array has non numeric keys. Support for unpacking array with string keys is available from PHP 8.1. To support 8.0 and below, you should call array_values first.
$c = ["a" => ["x" => "A", "y" => "B"], "b" => ["y" => "C", "z" => "D"]];
print_r(array_merge(...array_values($c)));
Array
(
[x] => A
[y] => C
[z] => D
)
Update: Based on comment by #MohamedGharib (for PHP 7.3.x and older ref)
This will throw an error if the outer array is empty, since array_merge would be called with zero arguments. It can be be avoided by adding an empty array as the first argument.
array_merge([], ...$a);
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
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
{
array_unshift($stack, ...$value);
}
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.
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.
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
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>
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]
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;
}
I believe this is the cleanest solution without using any mutations nor unfamiliar classes.
<?php
function flatten($array)
{
return array_reduce($array, function($acc, $item){
return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
}, []);
}
// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));
The Laravel helper for flattening arrays is Arr::flatten()
From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.
$flatArr = array_merge(...$originalArray);
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',
)
You can do it with ouzo goodies:
$result = Arrays::flatten($multidimensional);
See: Here
If you want to keep also your keys that is solution.
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Unfortunately it outputs only final nested arrays, without middle keys. So for the following example:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output is:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
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);
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.
How about using a recursive generator? https://ideone.com/d0TXCg
<?php
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
foreach (iterate($array) as $item) {
var_dump($item);
};
function iterate($array)
{
foreach ($array as $item) {
if (is_array($item)) {
yield from iterate($item);
} else {
yield $item;
}
}
}
/**
* 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
Anyone looking for a really clean solution to this; here's an option:
Taking an array of arrays with various key value configurations:
$test_array = array(
array('test' => 0, 0, 0, 0),
array(0, 0, 'merp' => array('herp' => 'derp'), 0),
array(0, 0, 0, 0),
array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ;
This will take only the values from each array and return a single flat array.
Output of values in result:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
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;
}
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;
}
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;
}
If you want to keep intermediate keys:
function flattenArray(array &$result, $value, string $key = "")
{
if (!is_array($value)) {
$result[$key] = $value;
return $result;
}
foreach ($value as $subKey => $subArray) {
$newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
flattenArray($result, $subArray, $newKey);
}
return $result;
}
$nestedArray = [
"name" => "John",
"pets" => [
["id" => 1, "name" => "snooop"],
["id" => 2, "name" => "medor"],
],
"job" => ["title" => "developper"],
];
$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);
This will output:
array(6) {
["name"]=>
string(4) "John"
["pets_0_id"]=>
int(1)
["pets_0_name"]=>
string(6) "snooop"
["pets_1_id"]=>
int(2)
["pets_1_name"]=>
string(5) "medor"
["job_title"]=>
string(10) "developper"
}
See https://ideone.com/KXLtzZ#stdout
Non recursive, non references based implementation, as asked, which may be easier to understand than a recursion based implemetation.
Can manage arbitrary deep multidimensional arrays, can't flatten associative arrays.
It works by flattening the array one level per cycle, until it is completly valid.
function array_flatten(): array{
$result = func_get_args();
// check all elements of $list are not arrays
$_is_flat = function (array $list): bool {
foreach ($list as $val) {
if (is_array($val)) {
return false;
}
}
return true;
};
do {
$tmp = [];
foreach ($result as $val) {
if (is_array($val)) {
if (!array_is_list($val)) {
throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
}
$tmp = array_merge($tmp, $val);
} else {
$tmp[] = $val;
}
}
$result = $tmp;
} while (!$_is_flat($result));
return $result;
}
This are the cases it handles:
assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');
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"
}
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;
}
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>";
<?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;
}
?>

Flatten a PHP array on Laravel [duplicate]

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().
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;
}
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
In PHP 5.6 and above you can flatten two dimensional arrays with array_merge after unpacking the outer array with ... operator. The code is simple and clear.
array_merge(...$a);
This works with collection of associative arrays too.
$a = [[10, 20], [30, 40]];
$b = [["x" => "A", "y" => "B"], ["y" => "C", "z" => "D"]];
print_r(array_merge(...$a));
print_r(array_merge(...$b));
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
)
Array
(
[x] => A
[y] => C
[z] => D
)
In PHP 8.0 and below, array unpacking does not work when the outer array has non numeric keys. Support for unpacking array with string keys is available from PHP 8.1. To support 8.0 and below, you should call array_values first.
$c = ["a" => ["x" => "A", "y" => "B"], "b" => ["y" => "C", "z" => "D"]];
print_r(array_merge(...array_values($c)));
Array
(
[x] => A
[y] => C
[z] => D
)
Update: Based on comment by #MohamedGharib (for PHP 7.3.x and older ref)
This will throw an error if the outer array is empty, since array_merge would be called with zero arguments. It can be be avoided by adding an empty array as the first argument.
array_merge([], ...$a);
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
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
{
array_unshift($stack, ...$value);
}
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.
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.
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
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>
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]
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;
}
I believe this is the cleanest solution without using any mutations nor unfamiliar classes.
<?php
function flatten($array)
{
return array_reduce($array, function($acc, $item){
return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
}, []);
}
// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));
The Laravel helper for flattening arrays is Arr::flatten()
From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.
$flatArr = array_merge(...$originalArray);
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',
)
You can do it with ouzo goodies:
$result = Arrays::flatten($multidimensional);
See: Here
If you want to keep also your keys that is solution.
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Unfortunately it outputs only final nested arrays, without middle keys. So for the following example:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output is:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
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);
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.
How about using a recursive generator? https://ideone.com/d0TXCg
<?php
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
foreach (iterate($array) as $item) {
var_dump($item);
};
function iterate($array)
{
foreach ($array as $item) {
if (is_array($item)) {
yield from iterate($item);
} else {
yield $item;
}
}
}
/**
* 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
Anyone looking for a really clean solution to this; here's an option:
Taking an array of arrays with various key value configurations:
$test_array = array(
array('test' => 0, 0, 0, 0),
array(0, 0, 'merp' => array('herp' => 'derp'), 0),
array(0, 0, 0, 0),
array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ;
This will take only the values from each array and return a single flat array.
Output of values in result:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
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;
}
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;
}
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;
}
If you want to keep intermediate keys:
function flattenArray(array &$result, $value, string $key = "")
{
if (!is_array($value)) {
$result[$key] = $value;
return $result;
}
foreach ($value as $subKey => $subArray) {
$newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
flattenArray($result, $subArray, $newKey);
}
return $result;
}
$nestedArray = [
"name" => "John",
"pets" => [
["id" => 1, "name" => "snooop"],
["id" => 2, "name" => "medor"],
],
"job" => ["title" => "developper"],
];
$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);
This will output:
array(6) {
["name"]=>
string(4) "John"
["pets_0_id"]=>
int(1)
["pets_0_name"]=>
string(6) "snooop"
["pets_1_id"]=>
int(2)
["pets_1_name"]=>
string(5) "medor"
["job_title"]=>
string(10) "developper"
}
See https://ideone.com/KXLtzZ#stdout
Non recursive, non references based implementation, as asked, which may be easier to understand than a recursion based implemetation.
Can manage arbitrary deep multidimensional arrays, can't flatten associative arrays.
It works by flattening the array one level per cycle, until it is completly valid.
function array_flatten(): array{
$result = func_get_args();
// check all elements of $list are not arrays
$_is_flat = function (array $list): bool {
foreach ($list as $val) {
if (is_array($val)) {
return false;
}
}
return true;
};
do {
$tmp = [];
foreach ($result as $val) {
if (is_array($val)) {
if (!array_is_list($val)) {
throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
}
$tmp = array_merge($tmp, $val);
} else {
$tmp[] = $val;
}
}
$result = $tmp;
} while (!$_is_flat($result));
return $result;
}
This are the cases it handles:
assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');
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"
}
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;
}
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>";
<?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;
}
?>

Categories