Skip to content

Instantly share code, notes, and snippets.

@SeanCannon
Last active March 8, 2024 11:38
Show Gist options
  • Star 44 You must be signed in to star a gist
  • Fork 25 You must be signed in to fork a gist
  • Save SeanCannon/6585889 to your computer and use it in GitHub Desktop.
Save SeanCannon/6585889 to your computer and use it in GitHub Desktop.
PHP array_flatten() function. Convert a multi-dimensional array into a single-dimensional array.
<?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;
}
@ultrasamad
Copy link

@georgeholt Ok. thanks.

@dhcmega
Copy link

dhcmega commented Apr 4, 2018

Will this achieve the same output?

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

Got it from 3rd solutions from https://stackoverflow.com/questions/1319903/how-to-flatten-a-multidimensional-array

@alexandrubb23
Copy link

alexandrubb23 commented May 24, 2018

Using variadic:
$result = array_merge(...$array);

@popovserhii
Copy link

@alexandrubb23
Your example doesn't work with an associative array. The error "Cannot unpack array with string keys" is thrown.

@cliffordp
Copy link

cliffordp commented Jan 29, 2019

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.

@belyas
Copy link

belyas commented Apr 1, 2019

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

@jamminjames
Copy link

@belyas, what does $arr represent in your code?

@mendezcode
Copy link

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

@jcicero518
Copy link

@belyas - very clever with the pass by reference of $flatten : ) Helped me make short work out of replacing nested foreach loops

@belyas
Copy link

belyas commented Apr 28, 2019

@jcicero518, Thanks, that's great to hear :)

@belyas
Copy link

belyas commented Apr 28, 2019

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

@sayhicoelho
Copy link

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

@belyas
Copy link

belyas commented May 11, 2019

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.

@kamalkhan
Copy link

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]

@larhemouchi
Copy link

Thank you

@maritaria
Copy link

Thanks!

@jchook
Copy link

jchook commented Nov 30, 2019

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]
    , []
  );
}

@SeanCannon
Copy link
Author

@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!

@g-rodigy
Copy link

g-rodigy commented Dec 9, 2019

@kamalkhan 👍

@jimitit
Copy link

jimitit commented Mar 10, 2021

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

@brandonmcconnell
Copy link

brandonmcconnell commented Jun 8, 2021

@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]

@bmsimo
Copy link

bmsimo commented Oct 4, 2022

@brandonmcconnell Thanks! Your answer is exactly what i was looking for!

@tiagofrancafernandes
Copy link

tiagofrancafernandes commented Jan 13, 2023

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",
  ]

@graceman9
Copy link

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

@sayhicoelho
Copy link

sayhicoelho commented Feb 15, 2024

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

@brandonmcconnell
Copy link

@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.

@graceman9
Copy link

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

@brandonmcconnell
Copy link

@graceman9 What data produces an unexpected result?

For infinite depth, just use -1

@graceman9
Copy link

@brandonmcconnell Have you noticed the difference?

{
    "a": "value1",
    "b_c": "value1.1",
}

and

{
    "a": "value1",
    "c": "value1.1",
}

@brandonmcconnell
Copy link

@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. 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment