94

It's probably beginner question but I'm going through documentation for longer time already and I can't find any solution. I thought I could use implode for each dimension and then put those strings back together with str_split to make new simple array. However I never know if the join pattern isn't also in values and so after doing str_split my original values could break.

Is there something like combine($array1, $array2) for arrays inside of multi-dimensional array?

Yi Jiang
  • 49,435
  • 16
  • 136
  • 136
Adriana
  • 8,464
  • 13
  • 36
  • 37
  • **Please check this link for solution** : http://stackoverflow.com/questions/14951811/i-want-to-add-sub-arrays-to-one-single-array-in-php/14952110#14952110 – Prasanth Bendra Feb 20 '13 at 05:12
  • 2
    **Another good reference question** with perhaps better answers: [How to Flatten a Multidimensional Array?](http://stackoverflow.com/q/1319903/367456) – hakre Apr 03 '15 at 06:12

23 Answers23

148
$array  = your array

$result = call_user_func_array('array_merge', $array);

echo "<pre>";
print_r($result);

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

Here is another solution (works with 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);
Prasanth Bendra
  • 31,145
  • 9
  • 53
  • 73
  • This answer is much faster than the accepted answer. – Roham Rafii Jan 28 '19 at 12:52
  • 15
    Since php5.3 you can now use the splat operator: `$result = array_merge(...$array);` https://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.splat – Redzarf Apr 27 '19 at 15:35
  • 1
    Your first answer doesn't work with a multi-dimensional array. https://3v4l.org/tY8vD – dearsina Jun 08 '19 at 06:08
  • 1
    And the second snippet fails on [this simple example](https://3v4l.org/dsSMi) -- it destroys data while pushing into the result. – mickmackusa Jun 19 '22 at 07:50
71

This is a one line, SUPER easy to use:

$result = array();
array_walk_recursive($original_array,function($v) use (&$result){ $result[] = $v; });

It is very easy to understand, inside the anonymous function/closure. $v is the value of your $original_array.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Arnold Roa
  • 7,335
  • 5
  • 50
  • 69
43

Use array_walk_recursive

<?php

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

$objTmp = (object) array('aFlat' => array());

array_walk_recursive($aNonFlat, create_function('&$v, $k, &$t', '$t->aFlat[] = $v;'), $objTmp);

var_dump($objTmp->aFlat);

/*
array(11) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  int(5)
  [5]=>
  int(6)
  [6]=>
  int(7)
  [7]=>
  int(8)
  [8]=>
  int(9)
  [9]=>
  int(10)
  [10]=>
  int(11)
}
*/

?>

Tested with PHP 5.5.9-1ubuntu4.24 (cli) (built: Mar 16 2018 12:32:06)

Luc M
  • 16,630
  • 26
  • 74
  • 89
  • Does anyone know why this doesn't work unless I use the (depreciated) call-time pass by reference. i.e. array_walk_recursive($array, create_function('&$v, $k, &$t', '$t[] = $v;'), &$flattened); The function definition is correctly defined as pass by reference. but doesn't work unless I pass by reference during call-time. – jon skulski Apr 28 '10 at 20:21
  • 2
    @jskilski Objects (`$objTmp` in this example) are passed by reference automatically; arrays are not. Try using an anonymous function (http://php.net/manual/en/functions.anonymous.php) instead of `create_function`. – dave1010 Aug 31 '11 at 11:57
  • 1
    this doesnt work in php 5.3.3 due to a bug in array_walk_recursive - https://bugs.php.net/bug.php?id=52719 – crazyphoton Jul 23 '12 at 05:09
  • 1
    @crazyphoton The kink says also `This bug has been fixed in SVN.` – Luc M Jul 23 '12 at 05:53
  • yup, but I had 5.3.3 on my server and spent a good amount of time trying to find out why it wasn't working. I just added it as an fyi for those running 5.3.3 :) – crazyphoton Jul 24 '12 at 06:57
  • Worked great for what I needed, had to flatten a multi-dimensional array using only 1 key per object, Had users but only needed an array of their userTokens, since I'm working with an ORM I had to flatten the ORM down to a basic array, with only the key I needed. Ended up with: (Notice the $v->token) array_walk_recursive($registrationIds, create_function('&$v, $k, &$t', '$t->aFlat[] = $v->token;'), $objTmp); – CTS_AE Dec 19 '13 at 19:28
  • create_function has bad performance and memory usage... http://php.net/manual/en/function.create-function.php. But what if we explicitly define the function? how will be the performance compare to others? – ken Oct 19 '14 at 09:00
  • 10
    Why does this answer mention using `array_values()`? I can't see any use of that function involved in the answer at all. – thomasrutter Sep 14 '16 at 05:21
  • Appreciate I'm late to the party, but here's a version that uses an anonymous function and doesn't randomly put the result into an object! ```array_walk_recursive($models, function($v, $k) use(&$return) { $return[] = $v;});``` – PeteAUK Jun 19 '20 at 10:22
34

If you specifically have an array of arrays that doesn't go further than one level deep (a use case I find common) you can get away with array_merge and the splat operator.

<?php

$notFlat = [[1,2],[3,4]];
$flat = array_merge(...$notFlat);
var_dump($flat);

Output:

array(4) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
}

The splat operator effectively changes the array of arrays to a list of arrays as arguments for array_merge.

Josh Johnson
  • 10,729
  • 12
  • 60
  • 83
  • 1
    This looks like the best answer to me. It won't work with string keys, but can be easily modified to do so: `$flat = array_merge( array_keys( $notFlat ), ...array_values( $notFlat ) );` – Ian Dunn Jun 26 '20 at 20:42
  • 1
    Worth noting that this only works in PHP 7.4+ – lachie_h Feb 15 '22 at 14:55
19
// $array = your multidimensional array

$flat_array = array();

foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $k=>$v){

$flat_array[$k] = $v;

}

Also documented: http://www.phpro.org/examples/Flatten-Array.html

CoolBeans
  • 20,654
  • 10
  • 86
  • 101
upallnite
  • 199
  • 1
  • 2
  • 2
    Note: Only use for arrays of primitives. "RecursiveArrayIterator treats all objects as having children, and tries to recurse into them." http://www.php.net/manual/en/class.recursivearrayiterator.php#106519 – ReactiveRaven Jan 31 '12 at 09:49
  • @hakre: +1 agreed: adding `iterator_to_array()` to this answer would negate the need for the `foreach` loop. It could be a simple one-liner function. (albeit a somewhat long one-line) – SDC Jul 19 '12 at 12:14
  • 3
    I know this is old but still useful, however the $k needs to be replaced by something unique, such as a counter. Just using $k causes elements to be removed if the names are the same in inner arrays as the main one. – Austin Best Oct 08 '14 at 20:19
11

Sorry for necrobumping, but none of the provided answers did what I intuitively understood as "flattening a multidimensional array". Namely this case:

[
  'a' => [
    'b' => 'value',
  ]
]

all of the provided solutions would flatten it into just ['value'], but that loses information about the key and the depth, plus if you have another 'b' key somewhere else, it will overwrite them.

I wanted to get a result like this:

[
  'a_b' => 'value',
]

array_walk_recursive doesn't pass the information about the key it's currently recursing, so I did it with just plain recursion:

function flatten($array, $prefix = '') {
    $return = [];
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $return = array_merge($return, flatten($value, $prefix . $key . '_'));
        } else {
            $return[$prefix . $key] = $value;
        }
    }
    return $return;
}

Modify the $prefix and '_' separator to your liking.

Playground here: https://3v4l.org/0B8hf

petrtvaruzek
  • 473
  • 5
  • 6
  • 1
    It appears that you want a different behavior versus what is being asked in this question. You want to create string keys which represent the path to the original value. There are probably better pages to post this answer on. – mickmackusa Jun 19 '22 at 07:52
6

With PHP 7, you can use generators and generator delegation (yield from) to flatten an array:

function array_flatten_iterator (array $array) {
    foreach ($array as $value) {
        if (is_array($value)) {
            yield from array_flatten_iterator($value);
        } else {
            yield $value;
        }
    }
}

function array_flatten (array $array) {
    return iterator_to_array(array_flatten_iterator($array), false);
}

Example:

$array = [
    1,
    2,
    [
        3,
        4,
        5,
        [
            6,
            7
        ],
        8,
        9,
    ],
    10,
    11,
];    

var_dump(array_flatten($array));

http://3v4l.org/RU30W

kelunik
  • 6,750
  • 2
  • 41
  • 70
5

A non-recursive solution (but order-destroying):

function flatten($ar) {
    $toflat = array($ar);
    $res = array();

    while (($r = array_shift($toflat)) !== NULL) {
        foreach ($r as $v) {
            if (is_array($v)) {
                $toflat[] = $v;
            } else {
                $res[] = $v;
            }
        }
    }

    return $res;
}
phihag
  • 278,196
  • 72
  • 453
  • 469
5
function flatten_array($array, $preserve_keys = 0, &$out = array()) {
    # Flatten a multidimensional array to one dimension, optionally preserving keys.
    #
    # $array - the array to flatten
    # $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
    # $out - internal use argument for recursion
    foreach($array as $key => $child)
        if(is_array($child))
            $out = flatten_array($child, $preserve_keys, $out);
        elseif($preserve_keys + is_string($key) > 1)
            $out[$key] = $child;
        else
            $out[] = $child;
    return $out;
}
chaos
  • 122,029
  • 33
  • 303
  • 309
5

Another method from PHP's user comments (simplified) and here:

function array_flatten_recursive($array) { 
   if (!$array) return false;
   $flat = array();
   $RII = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
   foreach ($RII as $value) $flat[] = $value;
   return $flat;
}

The big benefit of this method is that it tracks the depth of the recursion, should you need that while flattening.
This will output:

$array = array( 
    'A' => array('B' => array( 1, 2, 3)), 
    'C' => array(4, 5) 
); 
print_r(array_flatten_recursive($array)); 

#Returns: 
Array ( 
    [0] => 1 
    [1] => 2 
    [2] => 3 
    [3] => 4 
    [4] => 5 
)
SamGoody
  • 13,758
  • 9
  • 81
  • 91
  • 3
    Note: Only use for arrays of primitives. "RecursiveArrayIterator treats all objects as having children, and tries to recurse into them." http://www.php.net/manual/en/class.recursivearrayiterator.php#106519 – ReactiveRaven Jan 31 '12 at 09:51
4

In PHP>=5.3 and based on Luc M's answer (the first one) you can make use of closures like this

array_walk_recursive($aNonFlat, function(&$v, $k, &$t){$t->aFlat[] = $v;}, $objTmp);

I love this because I don't have to surround the function's code with quotes like when using create_function()

hisa_py
  • 929
  • 11
  • 16
  • 1
    if you're using anonymous functions, you might as well use a captured closure variable directly rather than this `objTemp` stuff – user102008 Jun 09 '12 at 08:02
  • 1
    There is a bug in PHP5.3.3 which causes this to crash - https://bugs.php.net/bug.php?id=52719 – crazyphoton Jul 23 '12 at 05:08
2

Using higher-order functions (note: I'm using inline anonymous functions, which appeared in PHP 5.3):

function array_flatten($array) {
    return array_reduce(
        $array,
        function($prev, $element) {
            if (!is_array($element))
                $prev[] = $element;
            else
                $prev = array_merge($prev, array_flatten($element));
            return $prev;
        },
        array()
    );
}
Paul d'Aoust
  • 3,019
  • 1
  • 25
  • 36
2

I found a simple way to convert multilevel array into one. I use the function "http_build_query" which converts the array into a url string. Then, split the string with explode and decode the value.

Here is a sample.

$converted = http_build_query($data);
$rows = explode('&', $converted);
$output = array();
foreach($rows AS $k => $v){
   list($kk, $vv) = explode('=', $v);
   $output[ urldecode($kk) ] =  urldecode($vv);
}
return $output;
SequenceDigitale.com
  • 4,038
  • 1
  • 24
  • 23
1

If you're okay with loosing array keys, you may flatten a multi-dimensional array using a recursive closure as a callback that utilizes array_values(), making sure that this callback is a parameter for array_walk(), as follows.

<?php  

$array = [1,2,3,[5,6,7]];
$nu_array = null;
$callback = function ( $item ) use(&$callback, &$nu_array) {
    if (!is_array($item)) {
    $nu_array[] = $item;
    }
    else
    if ( is_array( $item ) ) {
     foreach( array_values($item) as $v) {
         if ( !(is_array($v))) {
             $nu_array[] = $v;
         }
         else
         { 
             $callback( $v );
         continue;
         }    
     }
    }
};

array_walk($array, $callback);
print_r($nu_array);

The one drawback of the preceding example is that it involves writing far more code than the following solution which uses array_walk_recursive() along with a simplified callback:

<?php  

$array = [1,2,3,[5,6,7]];

$nu_array = [];
array_walk_recursive($array, function ( $item ) use(&$nu_array )
                     {
                         $nu_array[] = $item;
                     }
);
print_r($nu_array);

See live code

This example seems preferable to the previous one, hiding the details about how values are extracted from a multidimensional array. Surely, iteration occurs, but whether it entails recursion or control structure(s), you'll only know from perusing array.c. Since functional programming focuses on input and output rather than the minutiae of obtaining a result, surely one can remain unconcerned about how behind-the-scenes iteration occurs, that is until a perspective employer poses such a question.

slevy1
  • 3,797
  • 2
  • 27
  • 33
1

A new approach based on the previous example function submited by chaos, which fixes the bug of overwritting string keys in multiarrays:

# Flatten a multidimensional array to one dimension, optionally preserving keys.
# $array - the array to flatten
# $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
# $out - internal use argument for recursion

function flatten_array($array, $preserve_keys = 2, &$out = array(), &$last_subarray_found) 
{
        foreach($array as $key => $child)
        {
            if(is_array($child))
            {
                $last_subarray_found = $key;
                $out = flatten_array($child, $preserve_keys, $out, $last_subarray_found);
            }
            elseif($preserve_keys + is_string($key) > 1)
            {
                if ($last_subarray_found)
                {
                    $sfinal_key_value = $last_subarray_found . "_" . $key;
                }
                else
                {
                    $sfinal_key_value = $key;
                }
                $out[$sfinal_key_value] = $child;
            }
            else
            {
                $out[] = $child;
            }
        }

        return $out;
}

Example:
$newarraytest = array();
$last_subarray_found = "";
$this->flatten_array($array, 2, $newarraytest, $last_subarray_found);
xtrm
  • 966
  • 9
  • 22
1
/*consider $mArray as multidimensional array and $sArray as single dimensional array
this code will ignore the parent array
*/

function flatten_array2($mArray) {
    $sArray = array();

    foreach ($mArray as $row) {
        if ( !(is_array($row)) ) {
            if($sArray[] = $row){
            }
        } else {
            $sArray = array_merge($sArray,flatten_array2($row));
        }
    }
    return $sArray;
}
Yi Jiang
  • 49,435
  • 16
  • 136
  • 136
Iman
  • 11
  • 1
1

you can try this:

function flat_an_array($a)
{
    foreach($a as $i)
    {
        if(is_array($i)) 
        {
            if($na) $na = array_merge($na,flat_an_array($i));
            else $na = flat_an_array($i);
        }
        else $na[] = $i;
    }
    return $na;
}
TomSawyer
  • 3,711
  • 6
  • 44
  • 79
0

You can use the flatten function from Non-standard PHP library (NSPL). It works with arrays and any iterable data structures.

assert([1, 2, 3, 4, 5, 6, 7, 8, 9] === flatten([[1, [2, [3]]], [[[4, 5, 6]]], 7, 8, [9]]));
Ihor Burlachenko
  • 4,689
  • 1
  • 26
  • 25
0

Simple approach..See it via recursion..

<?php

function flatten_array($simple){
static $outputs=array();
foreach ( $simple as $value)
{
if(is_array($value)){
    flatten_array($value);
}
else{
    $outputs[]=$value;
}

}
return $outputs;
}

$eg=['s'=>['p','n'=>['t']]];
$out=flatten_array($eg);
print_r($out);

?>
  • Why is using `static` a potentially bad idea for this task? Unintended data retention. This will certainly catch programmers by surprise if they don't know/expect this behavior. [Look at this demonstration](https://3v4l.org/mSkNJ). – mickmackusa Jul 16 '18 at 00:52
  • You have posted a "code-only" answer -- these are low value on StackOverflow because they fail to educate the OP and future researchers. Please take a moment to improve this answer by including how your answer works and why you feel it is a better idea versus the earlier answers. – mickmackusa Jul 16 '18 at 00:54
0

Someone might find this useful, I had a problem flattening array at some dimension, I would call it last dimension so for example, if I have array like:

array (
  'germany' => 
  array (
    'cars' => 
    array (
      'bmw' => 
      array (
        0 => 'm4',
        1 => 'x3',
        2 => 'x8',
      ),
    ),
  ),
  'france' => 
  array (
    'cars' => 
    array (
      'peugeot' => 
      array (
        0 => '206',
        1 => '3008',
        2 => '5008',
      ),
    ),
  ),
)

Or:

array (
  'earth' => 
  array (
    'germany' => 
    array (
      'cars' => 
      array (
        'bmw' => 
        array (
          0 => 'm4',
          1 => 'x3',
          2 => 'x8',
        ),
      ),
    ),
  ),
  'mars' => 
  array (
    'france' => 
    array (
      'cars' => 
      array (
        'peugeot' => 
        array (
          0 => '206',
          1 => '3008',
          2 => '5008',
        ),
      ),
    ),
  ),
)

For both of these arrays when I call method below I get result:

array (
  0 => 
  array (
    0 => 'm4',
    1 => 'x3',
    2 => 'x8',
  ),
  1 => 
  array (
    0 => '206',
    1 => '3008',
    2 => '5008',
  ),
)

So I am flattening to last array dimension which should stay the same, method below could be refactored to actually stop at any kind of level:

function flattenAggregatedArray($aggregatedArray) {
    $final = $lvls = [];
    $counter = 1;
    $lvls[$counter] = $aggregatedArray;


    $elem = current($aggregatedArray);

    while ($elem){
        while(is_array($elem)){
            $counter++;
            $lvls[$counter] = $elem;
            $elem =  current($elem);
        }

        $final[] = $lvls[$counter];
        $elem = next($lvls[--$counter]);
        while ( $elem  == null){
            if (isset($lvls[$counter-1])){
                $elem = next($lvls[--$counter]);
            }
            else{
                return $final;
            }
        }
    }
}
ermacmkx
  • 439
  • 5
  • 12
-1

If you're interested in just the values for one particular key, you might find this approach useful:

function valuelist($array, $array_column) {
    $return = array();
    foreach($array AS $row){
        $return[]=$row[$array_column];
    };
    return $return;
};

Example:

Given $get_role_action=

array(3) {
  [0]=>
  array(2) {
    ["ACTION_CD"]=>
    string(12) "ADD_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
  [1]=>
  array(2) {
    ["ACTION_CD"]=>
    string(13) "LINK_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
  [2]=>
  array(2) {
    ["ACTION_CD"]=>
    string(15) "UNLINK_DOCUMENT"
    ["ACTION_REASON"]=>
    NULL
  }
}

than $variables['role_action_list']=valuelist($get_role_action, 'ACTION_CD'); would result in:

$variables["role_action_list"]=>
  array(3) {
    [0]=>
    string(12) "ADD_DOCUMENT"
    [1]=>
    string(13) "LINK_DOCUMENT"
    [2]=>
    string(15) "UNLINK_DOCUMENT"
  }

From there you can perform value look-ups like so:

if( in_array('ADD_DOCUMENT', $variables['role_action_list']) ){
    //do something
};
Jeromy French
  • 11,812
  • 19
  • 76
  • 129
-1

any of this didnt work for me ... so had to run it myself. works just fine:

function arrayFlat($arr){
$out = '';
    foreach($arr as $key => $value){

        if(!is_array($value)){
            $out .= $value.',';
        }else{
            $out .= $key.',';
            $out .= arrayFlat($value);
        }

    }
    return trim($out,',');
}


$result = explode(',',arrayFlat($yourArray));
echo '<pre>';
print_r($result);
echo '</pre>';
VNicholson
  • 25
  • 4
  • 1
    This code-only answer doesn't work as desired. https://3v4l.org/U3bfp <-- proof This is the reason for my downvote. – mickmackusa Jul 16 '18 at 01:03
-1

Given multi-dimensional array and converting it into one-dimensional, can be done by unsetting all values which are having arrays and saving them into first dimension, for example:

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;
}
kenorb
  • 155,785
  • 88
  • 678
  • 743
  • 1
    I have downvoted this answer because it doesn't work on any version. https://3v4l.org/7cO9N (Proof) Also, `each()` is deprecated from php7.2. – mickmackusa Jul 16 '18 at 00:59