-
-
Save SeanCannon/6585889 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* Convert a multi-dimensional array into a single-dimensional array. | |
* @author Sean Cannon, LitmusBox.com | seanc@litmusbox.com | |
* @param array $array The multi-dimensional array. | |
* @return array | |
*/ | |
function array_flatten($array) { | |
if (!is_array($array)) { | |
return false; | |
} | |
$result = array(); | |
foreach ($array as $key => $value) { | |
if (is_array($value)) { | |
$result = array_merge($result, array_flatten($value)); | |
} else { | |
$result = array_merge($result, array($key => $value)); | |
} | |
} | |
return $result; | |
} |
Using variadic:
$result = array_merge(...$array);
@alexandrubb23
Your example doesn't work with an associative array. The error "Cannot unpack array with string keys" is thrown.
Not sure to whom popovserhii's comment was directed, but I tested @georgeholt's "copy-pasta" and it worked splendidly https://3v4l.org/6fHhP:
$myarray = array('a', ['bla' => 0], 'b',[0,1], [0=>7], array(array(array('x'), 'y', 'z')), array(array('p')));
$res = array_flatten($myarray);
var_dump( $res);
array(10) {
[0]=>
string(1) "a"
["bla"]=>
int(0)
[1]=>
string(1) "b"
[2]=>
int(0)
[3]=>
int(1)
[4]=>
int(7)
[5]=>
string(1) "x"
[6]=>
string(1) "y"
[7]=>
string(1) "z"
[8]=>
string(1) "p"
}
For anyone else googling, https://davidwalsh.name/flatten-nested-arrays-php didn't work as well as this one.
Here's my solution while I was looking for it:
$multiDimArray = [[...], [...], [...]...];
$flatten = [];
$singleArray = array_map(function($arr) use (&$flatten) {
$flatten = array_merge($flatten, $arr);
}, $multiDimArray);
@belyas, what does $arr
represent in your code?
function array_flatten( $arr, $out=array() ) {
foreach( $arr as $item ) {
if ( is_array( $item ) ) {
$out = array_merge( $out, array_flatten( $item ) );
} else {
$out[] = $item;
}
}
return $out;
}
@belyas - very clever with the pass by reference of $flatten : ) Helped me make short work out of replacing nested foreach loops
@jcicero518, Thanks, that's great to hear :)
@jamminjames it's the current element that the callback receive from array_map as we pass our array as a second parameter, the function also accepts a callback as the first parameter so, that callback we receive the current element in the given array we passed as a second parameter, hope makes sense lol but it's simple ;)
Based on @belyas comment, I've created a function:
<?php
function array_flatten(array $multiDimArray): array {
$flatten = [];
$singleArray = array_map(function($arr) use (&$flatten) {
$flatten = array_merge($flatten, $arr);
}, $multiDimArray);
return $flatten;
}
$multiDimArray = [[1, 2], [3, 4]];
print_r(array_flatten($multiDimArray));
/*
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
*/
Cool @sayhicoelho,
All you need to do now is to check if the given array is a real array, I know you're using PHP7 so the type hinting will do work for you, but think about PHP5 devs.
We can use array_reduce
recursively to achieve this:
function array_flatten($items)
{
if (! is_array($items)) {
return [$items];
}
return array_reduce($items, function ($carry, $item) {
return array_merge($carry, array_flatten($item));
}, []);
}
array_flatten([1, [2, [3], 4], 5]); // [1, 2, 3, 4, 5]
Thank you
Thanks!
PHP 7.4
If you can use PHP 7.4, this version performs faster than @kamalkhan's suggestion.
Worth noting that @georgeholt's version out-performs the rest.
function array_flatten(array $items): array
{
return array_reduce($items,
fn($carry, $item) => is_array($item)
? [...$carry, ...array_flatten($item)]
: [...$carry, $item]
, []
);
}
@ultrasamad The issue is line 18 of the function. It should actually read:
... $result = array_merge($result, array($key => $value)); ...
And for those who want a copy-pasta of the working function:
function array_flatten($array = null) { $result = array(); if (!is_array($array)) { $array = func_get_args(); } foreach ($array as $key => $value) { if (is_array($value)) { $result = array_merge($result, array_flatten($value)); } else { $result = array_merge($result, array($key => $value)); } } return $result; }
Updated the gist, thanks for catching the bug!
$data = ['a', ['bla' => 0], 'b', [0, 1], [0 => 7], [[['x'], 'y', 'z']], [['p']]];
function flatten($data, $result = [])
{
foreach ($data as $flat) {
if (is_array($flat)) {
$result = flatten($flat, $result);
} else {
$result[] = $flat;
}
}
return $result;
}
flatten($data);
@georgeholt @ultrasamad @cliffordp I made a slight alteration to George's array_flatten()
function to support a $depth
parameter. With this parameter, the function will only flatten to the depth specified, or to the default $depth
value when the argument is not used.
For my example here, I am using a default $depth
of 1
, similar to JavaScript, however, you could change the default $depth
to -1
which will work virtually the same as using array.flat(Infinity)
in JavaScript, since negative values are truthy in PHP.
function array_flatten($array = null, $depth = 1) {
$result = [];
if (!is_array($array)) $array = func_get_args();
foreach ($array as $key => $value) {
if (is_array($value) && $depth) {
$result = array_merge($result, array_flatten($value, $depth - 1));
} else {
$result = array_merge($result, [$key => $value]);
}
}
return $result;
}
Here is an example using the sample array data Clifford posted earlier in this thread. I am using JSON encoding for returned values for readability:
$my_array = array('a', ['bla' => 0], 'b',[0,1], [0=>7], array(array(array('x'), 'y', 'z')), array(array('p'))); // sample data
$my_array; // the original array
// ["a",{"bla":0},"b",[0,1],[7],[[["x"],"y","z"]],[["p"]]]
array_flatten($my_array); // flatten only one layer by default
// {"0":"a","bla":0,"1":"b","2":0,"3":1,"4":7,"5":[["x"],"y","z"],"6":["p"]}
array_flatten($my_array, 2); // flatten two layers
// {"0":"a","bla":0,"1":"b","2":0,"3":1,"4":7,"5":["x"],"6":"y","7":"z","8":"p"}
array_flatten($my_array, -1); // flatten infinite number of layers
// {"0":"a","bla":0,"1":"b","2":0,"3":1,"4":7,"5":"x","6":"y","7":"z","8":"p"}
Here's a simpler example, using a non-associative array, returns again presented with JSON-encoded for readability.
$my_simple_array = [1,[2,3,[4,5,[6,7]]]]; // sample data
$my_array; // the original array
// [1,[2,3,[4,5,[6,7]]]]
array_flatten($my_array); // flatten only one layer by default
// [1,2,3,[4,5,[6,7]]]
array_flatten($my_array, 2); // flatten only one layer by default
// [1,2,3,4,5,[6,7]]
array_flatten($my_array, -1); // flatten infinite number of layers
// [1,2,3,4,5,6,7]
@brandonmcconnell Thanks! Your answer is exactly what i was looking for!
Working with associative arrays:
/**
* flatten function
*
* @param array $multiDimArray
* @return array
*/
function flatten(array $multiDimArray): array
{
$localFlatten = [];
foreach ($multiDimArray as $key => $value) {
if (\is_array($value)) {
foreach (flatten($value) as $subKey => $subValue) {
$localFlatten[$subKey] = $subValue;
}
continue;
}
$localFlatten[$key] = $value;
}
return $localFlatten;
}
Using
flatten([['abc' => 123, 'a' => 1, 'abc'], [2 => 'zxc']]);
// Result:
[
"abc" => 123,
"a" => 1,
0 => "abc",
2 => "zxc",
]
Here is what I was looking for:
function true_flatten(array $array, array $parents = [])
{
$return = [];
foreach ($array as $k => $value) {
$p = empty($parents) ? [$k] : [...$parents, $k];
if (is_array($value)) {
$return = [...$return, ...true_flatten($value, $p)];
} else {
$return[implode('_', $p)] = $value;
}
}
return $return;
}
Sample array:
$r = [
'a' => 'value1',
'b' => [
'c' => 'value1.1',
'd' => 'value1.2',
],
'e' => 'value2',
'f' => [
'j' => [
'k' => 'value2.1',
'm' => 'value2.2',
'n' => 'value2.3',
],
],
'o' => 'value3',
'p' => [
'some' => [
'very' => [
'deep' => [
'item' => [
'first',
'second',
]
]
]
]
],
'q' => 'value5',
]; // sample data
Output:
{
"a": "value1",
"b_c": "value1.1",
"b_d": "value1.2",
"e": "value2",
"f_j_k": "value2.1",
"f_j_m": "value2.2",
"f_j_n": "value2.3",
"o": "value3",
"p_some_very_deep_item_0": "first",
"p_some_very_deep_item_1": "second",
"q": "value5"
}
Nice @graceman9, but it doesn't work in PHP 7, as unpacking arrays with string keys was only implemented in PHP 8.
Fatal error: Uncaught Error: Cannot unpack array with string keys
Ref: https://wiki.php.net/rfc/array_unpacking_string_keys
I just changed ...
with array_merge
to make your code compatible with older versions of PHP.
function true_flatten(array $array, array $parents = [])
{
$return = [];
foreach ($array as $k => $value) {
$p = empty($parents) ? [$k] : array_merge($parents, [$k]);
if (is_array($value)) {
$return = array_merge($return, true_flatten($value, $p));
} else {
$return[implode('_', $p)] = $value;
}
}
return $return;
}
Output:
Array
(
[a] => value1
[b_c] => value1.1
[b_d] => value1.2
[e] => value2
[f_j_k] => value2.1
[f_j_m] => value2.2
[f_j_n] => value2.3
[o] => value3
[p_some_very_deep_item_0] => first
[p_some_very_deep_item_1] => second
[q] => value5
)
Tested in PHP 7.4.9
@sayhicoelho Have you tried my approach above? It should work with associative and non-associative arrays, and be compatible with PHP 7+.
It also supports a depth parameter, so you can easily flatten only the top layer, the first N layers, or infinitely.
@brandonmcconnell Using my sample data it gives not what I expected.
By the way, how to use it with unlimited $depth? array_flatten($r, 999)
doesn't look good for me
Despite of that your function does the job of flattening, no doubt :) maybe flatten is not the right word because of ambiguity?
@sayhicoelho thanks for 7.x support!
echo json_encode(array_flatten($r, 9));
{
"a": "value1",
"c": "value1.1",
"d": "value1.2",
"e": "value2",
"k": "value2.1",
"m": "value2.2",
"n": "value2.3",
"o": "value3",
"0": "first",
"1": "second",
"q": "value5"
}
@graceman9 What data produces an unexpected result?
For infinite depth, just use -1
@brandonmcconnell Have you noticed the difference?
{
"a": "value1",
"b_c": "value1.1",
}
and
{
"a": "value1",
"c": "value1.1",
}
@graceman9 Oh you want the keys to be concatenated, so every key keeps the log of its original path. Yeah, you are correct that my version does not do that. I tried to follow the approach common in other languages. 🙂
For a simple 2D array:
$multiDimArray = [[1, 2], [3, 4], [5, 6]];
$flatArray = array_reduce($multiDimArray, 'array_merge', []);
Will this achieve the same output?
Got it from 3rd solutions from https://stackoverflow.com/questions/1319903/how-to-flatten-a-multidimensional-array