This question already has answers here:
How to Flatten a Multidimensional Array?
(31 answers)
Closed 1 year ago.
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?
$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);
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.
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)
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.
// $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
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
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
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;
}
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;
}
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
)
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()
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()
);
}
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;
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);
/*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;
}
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;
}
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.
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]]));
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);
?>
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;
}
}
}
}
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
};
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>';
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;
}
Related
I have a array like this
Array (
[operator_15] => 3
[fiter_15] => 4
[operator_17] => 5
[fiter_17] => 5
[operator_19] => 4
[fiter_19] => 2
)
I want to separate this array in to 2 arrays:
key starting from fiter_
key starting from operator_
I used array filter and it doesn't work. any other option?
$array = array_filter(
$fitered_values,
function($key) {
return strpos($key, 'fiter_') === 0;
}
);
Just loop the array and substring what is before the _ with strpos and substr then you can filter them to a new array as this.
This method will also work with new array keys, see example:
$arr = array ( "operator_15" => 3,
"fiter_15" => 4,
"operator_17" => 5,
"fiter_17" => 5,
"somethingelse_12" => 99 // <--- Notice this line.
);
foreach($arr as $key => $val){
$subarr = substr($key,0, strpos($key, "_"));
$new[$subarr][$key] = $val;
}
var_dump($new);
output:
array(3) {
["operator"]=>
array(2) {
["operator_15"]=>
int(3)
["operator_17"]=>
int(5)
}
["fiter"]=>
array(2) {
["fiter_15"]=>
int(4)
["fiter_17"]=>
int(5)
}
["somethingelse"]=> // <-- is here now in it's own group with no code added
array(1) {
["somethingelse_12"]=>
int(99)
}
}
Give a try with below and see if its solve your problem
$array = array (
'operator_15' => 3,
'fiter_15' => 4,
'operator_17' => 5,
'fiter_17' => 5,
'operator_19' => 4,
'fiter_19' => 2 );
$operator=array();
$filter=array();
foreach($array as $key => $value){
if (strpos($key, 'operator_') !== false) {
$operator[$key] = $value;
}
if (strpos($key, 'fiter_') !== false) {
$filter[$key] = $value;
}
}
print_r($operator);
print_r($filter);
While iterating your array, populate a new array with first level (grouping) keys based on the prefix (substring before the underscore), then push the original associative data into that group.
Code: (Demo)
$result = [];
foreach ($array as $k => $v) {
$result[strtok($k, '_')][$k] = $v;
}
var_export($result);
It is suboptimal programming to declare individual variables because this removes the convenience of being able to easily iterate related data (related by structure).
The above snippet will allow you to iterate $result and access all data sets or you can individually access a particular subset like $result['fiter'].
This is a working example:
$a = array ( 'operator_15' => 3, 'fiter_15' => 4, 'operator_17' => 5, 'fiter_17' => 5, 'operator_19' => 4, 'fiter_19' => 2 );
$fiter_array = array();
$operator_array = array();
foreach($a as $key => $val)
{
if(strpos($key, 'fiter') !== false)
{
array_push($fiter_array, $a[$key]);
// or if you want to maintain the key
$fiter_array[$key] = $val;
}
else
{
array_push($operator_array, $a[$key]);
// or if you want to maintain the key
$operator_array[$key] = $val;
}
};
var_dump($fiter_array);
var_dump($operator_array);
This question already has answers here:
How to Flatten a Multidimensional Array?
(31 answers)
Closed 1 year ago.
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?
$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);
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.
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)
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.
// $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
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
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
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;
}
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;
}
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
)
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()
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()
);
}
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;
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);
/*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;
}
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;
}
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.
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]]));
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);
?>
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;
}
}
}
}
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
};
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>';
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;
}
In another thread i asked about flatten an array with a specific style to get something like this:
array(4) {
["one"]=> string(9) "one_value"
["two-four"]=> string(10) "four_value"
["two-five"]=> string(10) "five_value"
["three-six-seven"]=> string(11) "seven_value"
}
I've got some very good help there, but now im wondering how would i reverse this method to get again the same original array:
array(
'one' => 'one_value',
'two' => array
(
'four' => 'four_value',
'five' => 'five_value'
),
'three' => array
(
'six' => array
(
'seven' => 'seven_value'
)
)
)
I've tried with recursive method but with no luck.
I thank all the help in advance!
function expand($flat) {
$result = array();
foreach($flat as $key => $val) {
$keyParts = explode("-", $key);
$currentArray = &$result;
for($i=0; $i<count($keyParts)-1; $i++) {
if(!isSet($currentArray[$keyParts[$i]])) {
$currentArray[$keyParts[$i]] = array();
}
$currentArray = &$currentArray[$keyParts[$i]];
}
$currentArray[$keyParts[count($keyParts)-1]] = $val;
}
return $result;
}
Note that the code above is not tested and is given only to illustrate the idea.
The & operator is used for $currentArray to store not the value but the reference to some node in your tree (implemented by multidimensional array), so that changing $currentArray will change $result as well.
Here is an efficient recursive solution:
$foo = array(
"one" => "one_value",
"two-four" => "four_value",
"two-five" => "five_value",
"three-six-seven" => "seven_value"
);
function reverser($the_array) {
$temp = array();
foreach ($the_array as $key => $value) {
if (false != strpos($key, '-')) {
$first = strstr($key, '-', true);
$rest = strstr($key, '-');
if (isset($temp[$first])) {
$temp[$first] = array_merge($temp[$first], reverser(array(substr($rest, 1) => $value)));
} else {
$temp[$first] = reverser(array(substr($rest, 1) => $value));
}
} else {
$temp[$key] = $value;
}
}
return $temp;
}
print_r(reverser($foo));
strstr(___, ___, true) only works with PHP 5.3 or greater, but if this is a problem, there's a simple one-line solution (ask if you'd like it).
I have an array like this:
$months = Array (
"may" =>
Array (
"A" => 101,
"B" => 33,
"C" => 25
),
"june" =>
Array (
"A" => 73,
"B" => 11,
"D" => 32
),
"july" =>
Array (
"A" => 45,
"C" => 12
)
);
I want to get an array like this:
Array ( ['all'] =>
Array (
[A] => 219
[B] => 44
[C] => 37
[D] => 32
)
)
I wrote a function with 2 parameters (the two arrays to join) and it worked, but I fail, when I try to make it possible to call it with more than 2 arrays. I tried to do it via recursion:
function array_merge_elements(){
$arg_list = func_get_args();
$array1 = $arg_list[0];
$array2 = $arg_list[1];
$keys = array_unique(array_merge(array_keys($array1), array_keys($array2)));
$result_array = array();
foreach($keys as $key) {
$result_array["$key"] = 0;
if(!empty($array1[$key])) {
$result_array["$key"] += $array1[$key];
}
if(!empty($array2[$key])) {
$result_array["$key"] += $array2[$key];
}
}
if(func_num_args() == 2) {
return $result_array;
} else {
unset($arg_list[0]);
unset($arg_list[1]);
return array_merge_elements($result_array, $arg_list);
}
}
The problem seems to be, that calling the function with (array1, arglist) is not the same as calling the function with (array1, array2, array3) etc.
What's wrong with just doing (demo)
foreach ($months as $month) {
foreach ($month as $letter => $value) {
if (isset($months['all'][$letter])) {
$months['all'][$letter] += $value;
} else {
$months['all'][$letter] = $value;
}
}
}
print_r($months['all']);
or - somewhat less readable due to the ternary operation (demo):
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($months));
foreach ($iterator as $letter => $value) {
isset($months['all'][$letter])
? $months['all'][$letter] += $value
: $months['all'][$letter] = $value;
}
print_r($months['all']);
If you'd split off the first two entries of your found arguments; you can use the resulting array in a call with this function: Call_user_func_array
for fellow googlers out there, here's the answer to the original question
Assume we have a function that adds two arrays together:
function array_plus($a, $b) {
foreach($b as $k => $v)
$a[$k] = (isset($a[$k]) ? $a[$k] : 0) + $v;
return $a;
}
this is how to apply this function to a set of arrays
$sum = array_reduce($months, 'array_plus', array());
This question already has answers here:
How to Flatten a Multidimensional Array?
(31 answers)
Closed 1 year ago.
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?
$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);
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.
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)
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.
// $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
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
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
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;
}
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;
}
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
)
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()
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()
);
}
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;
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);
/*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;
}
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;
}
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.
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]]));
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);
?>
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;
}
}
}
}
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
};
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>';
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;
}