如何将多维数组扁平化?

374

在PHP中,是否有可能在不使用递归或引用的情况下将(双/多)维数组展开?

我只对值感兴趣,可以忽略键,我考虑使用array_map()array_values()来实现。


18
为什么要避免使用递归?递归指的是一个函数在其内部调用自身。虽然递归能够解决某些问题,但它也有缺点。首先,递归需要更多的内存空间,因为每次函数调用时都会创建一个新的栈帧。如果递归层数过多,就会导致栈溢出。此外,递归通常比迭代执行速度慢,因为每次调用都需要一定的开销。因此,在编写代码时,应该尽可能避免使用递归,除非递归是必须的或者使用递归可以使代码更加清晰易懂。 - JorenB
6
主要是重复的问题 https://dev59.com/oXRB5IYBdhLWcg3wv5o_ - cletus
4
如果你想处理任意深度的数组中的所有元素,没有递归是不可能的(虽然你可以把它伪装成迭代,但本质上并没有区别)。如果你只是想避免自己编写递归处理代码,可以使用http://dk2.php.net/manual/en/function.array-walk-recursive.php函数,并提供一个回调函数将元素添加到可用的数组中(使用全局变量、userdata参数、将其全部放在一个类中并引用$this等方法均可) 。 - Michael Madsen
@JorenB:我希望能看到一个实现可以被实现的情况。 - Alix Axel
请查看Nspl中的flatten函数。您还可以使用它来指定深度。 - Ihor Burlachenko
31个回答

7
这个解决方案是非递归的。请注意,元素的顺序会有些混乱。
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;
}

1
聪明的想法,但有一个漏洞。"$array[] = $value"并不会将$value的所有元素添加到$array中,它只是添加$value本身。如果您运行此代码,它将无限循环。 - Todd Owen
是的,从数组中移除值并将其再次附加到末尾并没有太多意义。我猜你想使用array_merge()代替? - deceze

7

6

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

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

3
您可以使用Ouzo好物来完成此操作:
 $result = Arrays::flatten($multidimensional);

查看:这里


3

尝试下面的简单函数:

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

也许你应该检查一下你的函数...似乎没有按预期工作。 - Emiliano
@Emiliano 试着提出一个新问题,也许你的输入数据不同,所以在你的特定情况下它不起作用。 - kenorb
我们有几个问题,每个问题都是一个已弃用的函数,你可以改进一下,你不是新来的,应该知道这点。第二,如果你的代码只能在特定版本的PHP上运行,请说明。第三,如果不能处理所有数据,请说明。 - Emiliano

3
如何使用递归生成器?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;
        }
    }
}

3

如果您想同时保留您的密钥,这是解决方案。

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
)

这正是我正在寻找的。我将函数名称更改为 flatten(),以匹配第二个示例。 - Manngo
有没有可能返回如下形式的数组: Array ( [sweet__a] => apple [sweet__b] => banana [sour] => lemon ) - Eddy MERCIER

2
技巧在于通过引用传递源数组和目标数组。
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);

2
如果你真的不喜欢递归...尝试移位代替 :)
$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];
    }
}

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


这是一个有趣的方法。与其他解决方案相比,它编辑了原始数组($a)。如果你用continue替换它,它会有一些加速。 - pcarvalho

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


这似乎只支持二维数组。 - Alix Axel
你是对的。使用它没有意义。我认为“太多php”的答案是最好的解决方案。 - Arsham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接