如何展平多维数组?

Ali*_*xel 241 php arrays flatten multidimensional-array

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

我只对值感兴趣所以可以忽略键,我正在考虑array_map()array_values().

too*_*php 277

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

function flatten(array $array) {
    $return = array();
    array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
    return $return;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你想要键,函数flatten(array $ array){$ return = array(); array_walk_recursive($ array,function($ a,$ b)use(&$ return){$ return [$ b] = $ a;}); 返回$ return; } (33认同)
  • @Alex很遗憾你需要使用`use`语法来使用`array_walk_recursive`,因为它不会通过引用接受可选的`$ userdata`参数 (2认同)
  • @Sebastian Piskorski这是因为你的值被视为键,所以只要在数组中引入自己的key => value对,第一个索引位置的数组值就会被视为没有值的键,因为键有要是唯一的,两个键匹配,您的值将添加到同一个键.一个简单的解决方案是首先对数组进行排序.这是PHP中固有的行为. (2认同)

Vol*_*erK 265

您可以使用标准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, " ";
}
Run Code Online (Sandbox Code Playgroud)

版画

1 2 3 4 5 6 7 8 9 
Run Code Online (Sandbox Code Playgroud)

  • 我是唯一一个认为'RecursiveIteratorIterator'是一个愚蠢名字的人吗? (330认同)
  • 它比"吸引人"更具"逻辑性".并非所有东西都可以拥有像JOGL,Knol或Azure这样出色的名字:-) (44认同)
  • `iterator_to_array($ it,false)`避免了对foreach的需要. (42认同)
  • 这对于作为子项的空数组不起作用.他们将作为父母返回. (5认同)
  • @AlixAxel:如您的示例所示,不提供空的叶子节点.他们*可以*作为值(在他们的`array_values`中).注意它与人们期望/需要的东西有很大关系. (2认同)
  • 这个问题是它会尝试迭代叶子对象.解决方案是扩展RecursiveArrayIterator https://gist.github.com/tcz/5618763 (2认同)
  • 在其他人提出的基础上,我能够制作这个小帮手:`function flatten($ arr){$ it = new RecursiveIteratorIterator(new RecursiveArrayIterator($ arr)); return iterator_to_array($ it,true); 希望这有助于其他人. (2认同)
  • 使用“iterator_to_array($it, false);”时要小心!如果此参数未设置或设置为 TRUE,重复的键将被**覆盖**!!!这意味着这将返回 **5 6 7 9** 而不是预期的 **1 2 3 4 5 6 7 8 9** 。 (2认同)

Pra*_*dra 85

二维数组的解决方案

请试试这个:

$array  = your array

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

echo "<pre>";
print_r($result);
Run Code Online (Sandbox Code Playgroud)

编辑:21年8月21日

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

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);
Run Code Online (Sandbox Code Playgroud)

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

  • 这是一个糟糕的策略.`call_user_func_array('array_merge',[])`(注意空数组)返回null并触发php警告错误.如果你知道你的数组不会是空的,那么这是一个灵巧的解决方案,但这并不是许多人可以做出的常见假设. (6认同)

Joy*_*abu 34

在PHP 5.6及更高版本中,您可以array_merge在使用...运算符解包外部数组之后展平二维数组.代码简单明了.

array_merge(...$a);
Run Code Online (Sandbox Code Playgroud)

但是当外部数组具有非数字键时,它不起作用.在这种情况下,您必须先打电话array_values.

$a = [[10, 20], [30, 40]];
$b = [["x" => "X", "y" => "Y"], ["p" => "P", "q" => "Q"]];

print_r(array_merge(...$a));
print_r(array_merge(...$b));

Array
(
    [0] => 10
    [1] => 20
    [2] => 30
    [3] => 40
)
Array
(
    [x] => X
    [y] => Y
    [p] => P
    [q] => Q
)
Run Code Online (Sandbox Code Playgroud)

  • 如果外部数组为空,则会抛出错误,如果与空数组 `array_merge([], ...$a);` 结合使用,则可以避免错误 (5认同)
  • 仅当数组的每个元素都是数组时才有效。如果数组包含混合类型,例如标量,则会发生错误。 (3认同)

hak*_*kre 24

要平移无递归(如您所要求的那样),您可以使用堆栈.当然,你可以把它变成它自己的功能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
        {
            $stack = array_merge(array_values($value), $stack);
        }
        else // a value to take
        {
           $flat[] = $value;
        }
    }
    return $flat;
}
Run Code Online (Sandbox Code Playgroud)

元素按其顺序处理.由于子元素将在堆栈顶部移动,因此将在下一步处理它们.

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

我不是特别肯定,但我过去曾经测试过这个:它RecurisiveIterator确实使用了递归,所以它取决于你真正需要的东西.应该可以基于堆栈创建递归迭代器:

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

演示

到目前为止,我还没有实现基于RecursiveIterator我认为是个好主意的堆栈.


War*_*rbo 18

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

array_reduce($my_array, 'array_merge', array());
Run Code Online (Sandbox Code Playgroud)

编辑:请注意,这可以组成扁平化任意数量的级别.我们可以通过几种方式做到这一点:

// 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))));
Run Code Online (Sandbox Code Playgroud)

当然,我们也可以使用循环,但问题是沿array_map或array_values的行要求组合函数.


nil*_*amo 17

使用递归.希望看到它是多么复杂,一旦你看到它的复杂性,你对递归的恐惧就会消散.

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>';
Run Code Online (Sandbox Code Playgroud)

输出:

<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>
Run Code Online (Sandbox Code Playgroud)

  • 这次递归的+1:希望看到它的复杂性如何,一旦你看到它的复杂性,你对递归的恐惧就会消散. (11认同)

All*_*toc 17

直截了当,一线答案.

function flatten_array(array $array)
{
    return iterator_to_array(
         new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)));
}
Run Code Online (Sandbox Code Playgroud)

用法:

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

print_r( flatten_array($array) );
Run Code Online (Sandbox Code Playgroud)

输出(在PsySH中):

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

现在,你现在很高兴你将如何处理密钥.干杯


编辑(2017-03-01)

引用Nigel Alderton的担忧/问题:

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

引用斯维什的回答:

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


Mah*_*r13 9

用于展平数组的Laravel助手是Arr::flatten()

use Illuminate\Support\Arr;
$flat = Arr::flatten([
    'a' => 'Level 1',
    'b' => [
        'Level 2 #1',
        'Level 2 #2',
    ],
]);
// $flat == ['Level 1', 'Level 2 #1', 'Level 2 #2']
Run Code Online (Sandbox Code Playgroud)

Arr::flatten() 中的第二个参数是深度,默认为 null。这意味着默认情况下数组将被无限展平。深度为 1 会将数组展平一级,依此类推。

$depth = 1;
$flat = Arr::flatten(['a','b',['c','d',['e','f']]]], $depth);
// $flat == ['a','b','c','d',['e','f']]
Run Code Online (Sandbox Code Playgroud)


小智 8

从 PHP v7.4 开始,您可以使用扩展运算符并合并数组。简单有效。

$flatArr = array_merge(...$originalArray);
Run Code Online (Sandbox Code Playgroud)

  • 或者,如果原始数组是关联数组,则 ```$flatArr = array_merge(...array_values($originalArray));``` (2认同)

art*_*pro 7

仅展平二维数组:

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

// Result: [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)


too*_*php 5

该解决方案是非递归的。请注意,元素的顺序将有所不同。

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;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

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

<?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));
Run Code Online (Sandbox Code Playgroud)