372

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().

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Alix Axel
  • 151,645
  • 95
  • 393
  • 500
  • 17
    Why avoid recursion? – JorenB Aug 23 '09 at 23:53
  • 6
    Dupe (mostly) http://stackoverflow.com/questions/526556/how-to-flatten-a-multi-dimensional-array-to-simple-one-in-php – cletus Aug 23 '09 at 23:53
  • 4
    You can't do anything with all elements of an arbitrarily deep arrays without recursion (you can disguise it as iteration, but potato, potahto.) If you just want to avoid writing the recursion handling code yourself, use http://dk2.php.net/manual/en/function.array-walk-recursive.php with a callback that adds the element to an available array (use global, the userdata parameter, put it all in a class and refer to $this, etc.) – Michael Madsen Aug 24 '09 at 00:05
  • @JorenB: I would like to see a implementation could be archived. – Alix Axel Aug 24 '09 at 01:42
  • Have a look at [flatten](https://github.com/ihor/Nspl#flattensequence-depth--null) function from [Nspl](https://github.com/ihor/Nspl). You also can specify a depth with it. – Ihor Burlachenko Feb 24 '16 at 20:34

31 Answers31

359

A more recent solution can be found in this answer below

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;
}
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
too much php
  • 88,666
  • 34
  • 128
  • 138
  • 40
    if you want keys,function flatten(array $array) { $return = array(); array_walk_recursive($array, function($a,$b) use (&$return) { $return[$b] = $a; }); return $return; } – Brendon-Van-Heyzen Jul 05 '11 at 16:40
  • can you rewrite this to use with php 5.2? – Alex Oct 01 '13 at 08:17
  • 2
    @Alex unfortunately you need the `use` syntax to make this work with `array_walk_recursive` since it won't accept the optional `$userdata` parameter by reference – Tim Seguine Feb 04 '14 at 09:14
  • 1
    Look like works fine for such Arrays -> http://ideone.com/DsmApP But not for such ones -> http://ideone.com/5Kltva Or is it me? – Sebastian Piskorski Sep 19 '14 at 10:23
  • 2
    @Sebastian Piskorski it's because your values are being treated like keys, so as soon as you introduce your own key=>value pair in an array, your array values in the first index position are treated like keys with no values, and because keys have to be unique, where two keys match, your values get added to the same key. A simple solution would be to sort the array first. This is behaviour inherent in PHP. – Martyn Shutt Sep 19 '15 at 13:28
  • Any reason you're passing the `return` array by reference explicitly? I thought arrays are passed by reference already. Tried without passing by reference and it works the same. Am I missing something? – samrap Apr 01 '16 at 17:08
  • +1, after testing various algorithms, this one is fastest. Twice as fast as second best - RecursiveIteratorIterator – gadelat Oct 20 '17 at 22:21
  • @samrap No, arrays are passed by value in PHP. But for better performance PHP internally uses a reference until something is changed in the array. Then it creates a copy and changes are only applied to the copy. – stollr Jul 18 '18 at 14:06
  • is it possible to keep the keys of Simple empty arrays(multi-dimensional unassociated arrays)? – Gunnrryy Jan 14 '19 at 12:21
  • `array_walk_recursive($array, static function($a) use (&$return) { $return[] = $a; });` making the anonymous function static is a memory and efficiency improvement to this original code. – Martin May 17 '23 at 16:00
333

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 
VolkerK
  • 95,432
  • 20
  • 163
  • 226
  • 439
    Am I the only one that thinks 'RecursiveIteratorIterator' is a silly name? – nilamo Aug 24 '09 at 06:51
  • 53
    It's more "logical" than "catchy". Not everything can have a fantastic name like JOGL, Knol or Azure :-) – VolkerK Aug 24 '09 at 08:35
  • 9
    This won't work for empty arrays as children. They will be returned as a parent. – hakre Oct 22 '11 at 15:30
  • 50
    `iterator_to_array($it, false)` avoids the need for the foreach. – Alix Axel Feb 20 '13 at 14:17
  • @hakre: Can you provide an example? I'm not following, http://codepad.org/w8aqaTBF... – Alix Axel Feb 20 '13 at 16:08
  • 2
    @AlixAxel: As your example shows, the empty leafnodes are not offered. They *could* qualify as values (in `array_values` they would). Take care that it has a lot to do with what one expects / needs here. – hakre Feb 20 '13 at 16:18
  • 2
    The problem with this is it will try to iterate over leaf objects too. The solution is to extend RecursiveArrayIterator https://gist.github.com/tcz/5618763 – gphilip May 21 '13 at 10:09
  • 9
    Building on what others presented, I was able to craft this little helper: `function flatten($arr){ $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr)); return iterator_to_array($it, true); }` Hope this helps others. – Mike S. Jan 10 '14 at 20:25
  • Sadly the solution throws objects away. – jakub_jo Mar 25 '17 at 00:27
  • this solution is the best, if you have a wide range of arrays ( assoc, mixed ... ) but is also slower. – pcarvalho Jan 27 '18 at 23:57
  • 1
    Watch out when using `iterator_to_array($it, false);`! If this parameter is not set or set to TRUE, duplicate keys will be **overwritten**!!! Meaning this would return **5 6 7 9** and not **1 2 3 4 5 6 7 8 9** as expected. – ponsfrilus Jan 15 '21 at 18:16
  • @MikeS. I prefer to cast the parameter as array: function flatten($arr){ $it = new RecursiveIteratorIterator(new RecursiveArrayIterator((array) $arr)); return iterator_to_array($it, true); } – Davide Iandoli Aug 18 '22 at 09:17
262

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);
mvorisek
  • 3,290
  • 2
  • 18
  • 53
Joyce Babu
  • 19,602
  • 13
  • 62
  • 97
  • 3
    This works ONLY when every element of the array is an array. If the array comprises mixed types, such as scalars, an error will occur. – Otheus Nov 26 '19 at 14:05
  • 1
    @Otheus That is because the above solution does not use recursion. As you said, it requires an array of array. But on the plus side, this should be much faster that the other methods, since it does not have the additional overhead of the function calls. – Joyce Babu Nov 26 '19 at 15:30
  • 8
    Will throw an error if the outer array is empty, could be avoidable if combined with an empty array `array_merge([], ...$a);` – Mohamed Gharib Jan 01 '20 at 01:38
  • @MohamedGharib Nice catch. – Joyce Babu Jan 01 '20 at 06:07
  • If using associative arrays you can check this solution https://stackoverflow.com/questions/40663687/cannot-unpack-array-with-string-keys – alex Jan 17 '20 at 15:40
  • @alex It is already mentioned in the answer. There is sample code too. – Joyce Babu Jan 18 '20 at 12:04
  • 1
    wow that's some cool wacky syntax.. 5 years of PHP programming and have never heard of this (...) operator. Works like a charm though! – Robert Sinclair Feb 29 '20 at 07:46
  • 1
    very cool. not right for 100% of situations, but perfect when it works. `$a = array_merge( array(), ...array_values( $a ) );` worked for me to flatten the results of querying is single column in wordpress with `$wpdb->get_results( $sql, ARRAY_N )` – squarecandy Dec 23 '20 at 20:59
  • Creative usage of unpacking operator! – AHHP Jul 15 '21 at 19:12
  • Best Answer Ever. – Govar Nov 28 '22 at 08:40
102

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

m1k1o
  • 2,344
  • 16
  • 27
Prasanth Bendra
  • 31,145
  • 9
  • 53
  • 73
  • Thank you, the first one worked on an array I was getting from PDO where the other solutions did not. – JAL Nov 30 '13 at 23:04
  • 7
    This is a poor strategy. `call_user_func_array('array_merge', [])` (notice the empty array) returns null and triggers a php warning error. It's a slick solution if you know for a fact your array won't be empty, but that's not a common assumption many can make. – goat Jan 15 '15 at 06:42
  • The OP specifically asked for non-recursive solutions. – Lux Jul 20 '17 at 20:27
  • Wow, cool 2d flattern! But to prevent notice just use `$result = $array ?call_user_func_array('array_merge', $array) : [];` – Alexander Goncharov Mar 04 '18 at 10:29
  • cool bruh, but don't you have by chance a counter-function array-deflatten? – FantomX1 Sep 18 '18 at 11:44
  • PHP8 throws Fatal error: Uncaught ArgumentCountError: array_merge() does not accept unknown named parameters in – Deepak Rajpal Sep 23 '21 at 07:49
  • 1
    The [2nd snippet](https://3v4l.org/EjO6a) cannot be trusted. It potentially destroys data. The [1st snippet](https://3v4l.org/8M1Lt) is ONLY suitable an array of arrays. Despite its score, this answer doesn't contain advice that is fit and reliable for general use. – mickmackusa Jun 19 '22 at 10:18
30

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.

Walf
  • 8,535
  • 2
  • 44
  • 59
hakre
  • 193,403
  • 52
  • 435
  • 836
  • +1 for outstanding array_flatten function. I had to add `if(!empty($value)){$flat[] = $value}` inside the else statement to prevent empty being added to the result array. Awesome function! – Alex Sarnowski Mar 23 '17 at 03:35
  • 1
    The flattening operation can be reduced from `$stack = array_merge(array_values($value), $stack);` to `array_unshift($stack, ...$value);` and the `array_values()` call is probably extraneous for most, but it didn't seem right to edit the answer given it predates v5.6. – Walf Jan 13 '21 at 03:15
  • @Walf: Yes, this is the only reason. Thanks for the hint thought, I've edited it in. – hakre Jan 13 '21 at 09:07
26

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.

Warbo
  • 2,611
  • 1
  • 29
  • 23
  • Multi-dimensional != bi-dimensional. – Alix Axel Jul 17 '13 at 17:29
  • @atamur This works on PHP 5.3+. As noted in the changelog for array_reduce, $initial could only be an integer before 5.3, then it was allowed to be "mixed" (ie. anything your reduction function supports) – Warbo Jul 19 '13 at 13:20
  • 1
    @AlixAxel You're right that multi-dimensional != bi-dimensional, but this can be composed to flatten any number of levels. One nice consequence of composing folds is that it obeys a fixed limit; if I have an array nested to 5 levels, I can `fold` it into 4 levels, or `fold . fold` it to get 3 levels, or `fold . fold . fold` it to get 2 levels, etc. This also prevents bugs getting hidden; eg. if I want to flatten a 5D array but I'm given a 4D array, the error will trigger immediately. – Warbo Jul 19 '13 at 13:35
  • I love this solution, for 2-dimensional arrays. Fits the bill perfectly. – Tom Auger Mar 12 '16 at 00:36
  • I agree that your single level definition is the best answer, it's also wonderfully neat. However I think you incorrectly named it `$concat`, I think you should just call it `$flatten`. `array_merge` is the php equivalent of concat. [I tried](https://bugs.php.net/bug.php?id=73576) to get `array_concat` added as an alias for `array_merge`. – icc97 Oct 29 '17 at 15:27
26

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

Community
  • 1
  • 1
Allen Linatoc
  • 624
  • 7
  • 17
  • 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`. – Nigel Alderton Jan 19 '17 at 18:36
  • 1
    @NigelAlderton Just add `false` as second parameter (`$use_keys`) to the [`iterator_to_array`](http://php.net/manual/en/function.iterator-to-array.php) call. – Svish Feb 26 '17 at 19:29
  • Watch out when using `iterator_to_array($it, false);`! If this parameter is not set or set to TRUE, duplicate keys will be **overwritten**!!! If you use it with `array(1,2,array(3,4, array(5,6,7), 8), 9);` this would return **5 6 7 9** and not **1 2 3 4 5 6 7 8 9** as expected. – ponsfrilus Jan 15 '21 at 18:19
  • This use of iterators has the benefit of preserving numeric 2nd level keys while flattening. Implement this approach using [the sample data from "Flatten a 2d array while preserving numeric associative row keys"](https://stackoverflow.com/q/75096836/2943403) to see the behavior which is different from other techniques on this page. Proof: https://3v4l.org/9rEm4 – mickmackusa Jan 13 '23 at 01:39
18

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>
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
nilamo
  • 1,932
  • 13
  • 22
  • 1
    I don't fear recursion, I just want to learn other ways to do the same. – Alix Axel Aug 24 '09 at 02:02
  • 13
    +1 for this recursion: Hopefully upon seeing how not-complex it is, your fear of recursion will dissipate once you see how not-complex it is. – oxygen Feb 05 '13 at 15:35
  • 1
    OK, this is over me. *How* it is possible, that reply ("I don't fear recursion") is **three and a half year older** (Aug 24 '09) than initial statement ("(...) your fear of recursion will dissipate (...)"), made on Feb 5 '13? – trejder Jun 20 '13 at 10:52
13

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]
artnikpro
  • 5,487
  • 4
  • 38
  • 40
7

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;
}
too much php
  • 88,666
  • 34
  • 128
  • 138
  • 1
    Clever idea, but there's a bug. "$array[] = $value" doesn't add all the *elements* of $value to $array, it merely add $value itself. If you run this code, it will loop indefinitely. – Todd Owen Aug 24 '09 at 00:29
  • Yes, `shifting` the value off the array and appending it again to the end doesn't make much sense. I guess you wanted to `array_merge()` instead? – deceze Aug 24 '09 at 00:37
6

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));
Dariush Alipour
  • 318
  • 3
  • 11
6

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

Mahoor13
  • 5,297
  • 5
  • 23
  • 24
5

From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.

$flatArr = array_merge(...$originalArray);
Robert Yeomans
  • 192
  • 1
  • 4
  • 1
    Or, if the original array is associative, ```$flatArr = array_merge(...array_values($originalArray));``` – Alliswell Mar 10 '23 at 08:40
3

You can do it with ouzo goodies:

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

See: Here

woru
  • 1,420
  • 9
  • 17
3

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',
)
kenorb
  • 155,785
  • 88
  • 678
  • 743
  • maybe you should check your function... seems like no work as expected – Emiliano Dec 11 '19 at 13:45
  • @Emiliano Try asking a new question, maybe your input data is different, so it won't work in your particular case. – kenorb Dec 11 '19 at 14:43
  • we have few issues each is a deprecated function, you can improve that point you weren't a new guy here, you should know it second if your code work with a specific version of php say it third if not work with all data say it – Emiliano Dec 12 '19 at 18:33
3

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;
        }
    }
}
Andriy
  • 973
  • 8
  • 13
3

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
)
Manngo
  • 14,066
  • 10
  • 88
  • 110
Jsowa
  • 9,104
  • 5
  • 56
  • 60
  • That’s exactly what I was looking for. I changed the function name to `flatten()` to match the second example. – Manngo Jan 27 '21 at 05:18
  • Is it ossible to have a return like : Array ( [sweet__a] => apple [sweet__b] => banana [sour] => lemon ) – Eddy MERCIER Jul 30 '21 at 10:01
2

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);
Rick Garcia
  • 167
  • 1
  • 4
2

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.

BurninLeo
  • 4,240
  • 4
  • 39
  • 56
  • this is an interesting approach. in contrast with the other solutions, it edits the original array ($a). If you replace it with a `continue`, its somewhat faster. – pcarvalho Jan 28 '18 at 00:21
1
/**
 * 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

Arsham
  • 1,432
  • 3
  • 19
  • 28
1

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
Lewis
  • 624
  • 9
  • 16
  • I did not downvote, but a reason I could imagine what may be considered not helpful with the answer is that this it is similar to the accepted answer post from 2009 which at least suggests the same. Please compare your own and comment. – hakre Oct 29 '21 at 08:07
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;
}
Alexei T
  • 710
  • 1
  • 7
  • 17
0

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;
}
ryanve
  • 50,076
  • 30
  • 102
  • 137
0

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;
}
Community
  • 1
  • 1
Gellweiler
  • 751
  • 1
  • 12
  • 25
0

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

Thykof
  • 895
  • 7
  • 9
0

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');
devsmt
  • 56
  • 1
  • 4
-1

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"
}
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • https://github.com/alixaxel/phunction/blob/7e8f47049d522609a414498f4dfd023dfa82a0d0/phunction.php#L269-L304 – Alix Axel Sep 30 '13 at 06:06
  • @AlixAxel How is this comment relative? Wrong post.. ? – Gajus Sep 30 '13 at 09:52
  • No. I thought it was pretty similar to what you are doing and decided to share it, I think the only difference is that my representation is valid PHP as well - of the form `$var['a']['b']['c'][0] = 'a'; ...`. – Alix Axel Sep 30 '13 at 12:00
  • I intentionally needed HTML output. Though thank you for sharing. – Gajus Sep 30 '13 at 12:52
  • @gajus-kuizinas Thats why I also posted this Answer:http://stackoverflow.com/a/30463922/1245992. No offence meant. – Gellweiler May 27 '15 at 15:59
  • 2
    I feel that this is the right answer to the wrong question. When answering, please attempt to answer the question as it is asked -- otherwise pages can depart from the core issue and leave future researchers confused. – mickmackusa Sep 19 '18 at 23:34
-1

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;
}
A.L
  • 10,259
  • 10
  • 67
  • 98
-1

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>";
Martyn Shutt
  • 1,671
  • 18
  • 25
  • 1
    Please include some explanation of how your snippet works and why it is a good idea. Code-only answers are low-value on StackOverflow because they do a poor job of educating/empowering the OP and future researchers. Remember, we are never ONLY speaking to the OP; old pages are used to close new pages, so pages need to be informative enough to solve issues for future askers as well. – mickmackusa Sep 19 '18 at 23:28
-1
<?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;
}
?>
Furqan Freed
  • 366
  • 1
  • 3
  • 9
-1

Here's a simplistic approach:

$My_Array = array(1,2,array(3,4, array(5,6,7), 8), 9);

function checkArray($value) {
    foreach ($value as $var) {
        if ( is_array($var) ) {
            checkArray($var);
        } else {
            echo $var;
        }
    }
}

checkArray($My_Array);
Jack
  • 3,271
  • 11
  • 48
  • 57