如何扁平化多维数组?

它是可能的,在PHP中,在不使用递归或引用的情况下扁平化(bi/multi)维数组?

我只对值感兴趣,所以键可以忽略,我认为在array_map()array_values()行。

372803 次浏览

这个解是非递归的。注意,元素的顺序会有些混合。

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

使用递归。希望在看到它有多简单的时候,你对递归的恐惧会在你看到它有多简单的时候消失。

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

输出:

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

对于PHP 5.3,最短的解决方案似乎是使用新的闭包语法的array_walk_recursive():

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

你可以使用标准PHP库(SPL)来“隐藏”递归。

$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, " ";
}

打印

1 2 3 4 5 6 7 8 9

为了平摊w/o递归(正如你所要求的那样),你可以使用堆栈。当然,你可以把它放到自己的函数中,比如array_flatten。下面是一个使用无键的版本:

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

元素按顺序处理。因为子元素将被移动到堆栈的顶部,所以它们将被下一步处理。

也可以考虑键,但是,您需要不同的策略来处理堆栈。这是必需的,因为您需要处理子数组中可能重复的键。类似的答案在一个相关的问题:遍历多维数组,同时保留键

我不是特别确定,但我在过去测试过:RecurisiveIterator确实使用递归,所以它取决于你真正需要什么。应该可以创建一个基于堆栈的递归迭代器:

foreach(new FlatRecursiveArrayIterator($array) as $key => $value)
{
echo "** ($key) $value\n";
}

Demo

到目前为止,我还没有实现基于RecursiveIterator的堆栈,我认为这是一个不错的想法。

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

诀窍是通过引用传递源数组和目标数组。

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

二维数组的解

请试试这个:

$array  = your array


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


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

编辑:8月21日-13日

下面是适用于多维数组的解决方案:

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

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

这个版本可以做深层、浅层或特定数量的层:

/**
* @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;
}

我只是想指出这是一个折叠,所以可以使用array_reduce

array_reduce($my_array, 'array_merge', array());

编辑:注意,这可以组成平任意数量的水平。我们可以通过以下几种方式做到这一点:

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

当然,我们也可以使用循环,但这个问题要求的是一个组合子函数,类似于array_map或array_values。

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

要点片段

我需要用HTML输入格式表示PHP多维数组。

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

如果你有一个对象数组,想用一个节点把它压平,只需要使用这个函数:

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

你可以用茴香烈酒糖果来实现:

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

看:在这里

因为在这里中的代码看起来很可怕。下面是一个将多维数组转换为html格式兼容语法的函数,但它更容易阅读。

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

这是我的解决方案,使用了一个参考:

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

试试下面这个简单的函数:

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

所以从这里:

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',
),
),
),
),
),
),
)

你会得到:

array (
'first_name' => 'First name',
'last_name' => 'Last name',
'thoroughfare' => 'Address 1',
'premise' => 'Address 2',
'locality' => 'Town/City',
'administrative_area' => 'County',
'postal_code' => 'Postcode',
)

直截了当的一行程序回答。

function flatten_array(array $array)
{
return iterator_to_array(
new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)));
}

用法:

$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];


print_r( flatten_array($array) );

输出(在PsySH):

Array
(
[name] => Allen Linatoc
[age] => 21
[0] => Call of Duty
[1] => Titanfall
[2] => Far Cry
)

现在就看你怎么处理钥匙了。干杯


编辑 (2017-03-01)

引用奈杰尔·奥尔德顿的关注点/问题:

澄清一下,这保留了键(甚至是数字键),因此具有相同键的值将丢失。例如,$array = ['a',['b','c']]变成Array ([0] => b, [1] => c )'a'丢失,因为'b'也有一个0的键

引用< a href = " https://stackoverflow.com/users/39321/svish " > Svish < / >的答案:

只需将false作为($use_keys)参数添加到iterator_to_array调用中

仅平坦二维数组:

$arr = [1, 2, [3, 4]];
$arr = array_reduce($arr, function ($a, $b) {
return array_merge($a, (array) $b);
}, []);


// Result: [1, 2, 3, 4]
<?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;
}
?>

如果你真的不喜欢递归……试着换一下位置吧。

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

注意:在这个简单版本中,它不支持数组键。

我相信这是最干净的解决方案,不使用任何突变或不熟悉的类。

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

这里有一个简单的方法:

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

在PHP 5.6及以上版本中,在使用...操作符解包外层数组后,可以使用array_merge将二维数组平展。代码简单明了。

array_merge(...$a);

这也适用于关联数组的集合。

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

在PHP 8.0及以下版本中,当外部数组具有非数字键时,数组解包将不起作用。PHP 8.1提供了对用字符串键解压数组的支持。要支持8.0及以下版本,您应该首先调用array_values

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

更新:根据@ mohammedgharib的评论

如果外层数组为空,这将抛出一个错误,因为array_merge将被调用时参数为零。可以通过添加一个空数组作为第一个参数来避免这种情况。

array_merge([], ...$a);

任何想要解决这个问题的人;这里有一个选择:

获取具有不同键值配置的数组的数组:

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

这将只从每个数组中获取值,并返回单个平面数组。

result值的输出:

0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0

使用递归生成器怎么样?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;
}
}
}

如果你想保住你的钥匙,那就是解决办法。

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

不幸的是,它只输出最后的嵌套数组,没有中间键。对于下面的例子:

$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));

输出是:

Array
(
[a] => apple
[b] => banana
[sour] => lemon
)

扁平化数组的Laravel helper是加勒比海盗:平()

如果你想保留中间键:

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

这将输出:

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

看到https://ideone.com/KXLtzZ#stdout

从PHP v7.4开始,可以使用展开操作符并合并数组。简单有效。

$flatArr = array_merge(...$originalArray);