如何在PHP中将多维数组“扁平化”为简单数组?


82

这可能是初学者的问题,但是我已经花了更长的时间阅读文档,而且找不到任何解决方案。我以为我可以对每个维度使用爆破,然后将这些字符串放回一起str_split以组成新的简单数组。但是我不知道联接模式是否也不在值中,因此在执行完str_split原始值之后,可能会损坏。

combine($array1, $array2)多维数组内部是否存在类似于数组的内容?


请检查该链接的解决方案stackoverflow.com/questions/14951811/...
Prasanth Bendra

1
另一个很好的参考问题,也许还有更好的答案:如何展平多维数组?
哈克(Hakre)2015年

Answers:


42

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

?>

使用PHP 5.5.9-1ubuntu4.24(CLI)测试(内置:2018年3月16日12:32:06)


没有人知道为什么这是行不通的,除非我使用(折旧的)通话时间传递作为参考。即array_walk_recursive($ array,create_function('&$ v,$ k,&$ t','$ t [] = $ v;'),&$ flattened); 函数定义正确定义为通过引用传递。但是除非我在通话期间通过引用传递了,否则无法使用。
jskulski 2010年

2
@jskilski对象($objTmp在本例中)是通过引用自动传递的;数组不是。尝试使用匿名函数(php.net/manual/en/functions.anonymous.php)代替create_function
2011年

1
由于array_walk_recursive中的错误,这在php 5.3.3中不起作用-bugs.php.net/bug.php?id=52719
crazyphoton 2012年

1
@crazyphoton纠结者还说This bug has been fixed in SVN.
卢克M

10
为什么这个答案提到使用array_values()?我完全看不到答案中涉及该功能的任何使用。
thomasrutter

131
$array  = your array

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

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

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

这是另一个解决方案(适用于多维数组):

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

这个答案比接受的答案快得多。
罗罕·拉菲

8
从php5.3开始,您现在可以使用splat运算符:$result = array_merge(...$array); php.net/manual/en/…–
Redzarf

您的第一个答案不适用于多维数组。3v4l.org/tY8vD
dearsina

53

这是一行,SUPER易于使用:

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

在匿名函数/闭包内部非常容易理解。$v是你的价值$original_array


4
这是两级数组中唯一对我有用的函数。
西普里安·特佩斯

18
// $array = your multidimensional array

$flat_array = array();

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

$flat_array[$k] = $v;

}

还记录了以下内容:http : //www.phpro.org/examples/Flatten-Array.html


2
注意:仅用于基元数组。“ RecursiveArrayIterator将所有对象都视为有子对象,并尝试递归到它们中。” php.net/manual/en/class.recursivearrayiterator.php#106519
ReactiveRaven

@hakre:+1同意:添加iterator_to_array()到此答案将不需要foreach循环。它可能是一个简单的单线函数。(虽然有些长的单行)
SDC 2012年

2
我知道这是旧的但仍然有用,但是$ k需要替换为唯一的东西,例如计数器。如果内部数组中的名称与主数组相同,则仅使用$ k会删除元素。
奥斯丁最佳

12

如果您专门拥有一个数组,且数组的深度不超过一个级别(我发现这种用例很常见),则可以使用array_mergesplat运算符。

<?php

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

输出:

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

splat运算符有效地将数组数组更改为数组列表作为的参数array_merge


这似乎是对我最好的答案。它不能与字符串键一起使用,但是可以很容易地修改为:$flat = array_merge( array_keys( $notFlat ), ...array_values( $notFlat ) );
Ian Dunn

6

在PHP 7中,您可以使用生成器和生成器委托(yield from)来展平数组:

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

例:

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

var_dump(array_flatten($array));

http://3v4l.org/RU30W


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

很抱歉,但它似乎并没有妥善处理多维数组-演示
阿尔瓦罗·冈萨雷斯

5

从PHP的另一种方法用户评论(简体),并在这里

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

该方法的最大好处是,如果在展平时需要,它可以跟踪递归的深度。
这将输出:

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

3
注意:仅用于基元数组。“ RecursiveArrayIterator将所有对象都视为有子对象,并尝试递归到它们中。” php.net/manual/en/class.recursivearrayiterator.php#106519
ReactiveRaven

4

非递归解决方案(但破坏了订单):

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

4

在PHP> = 5.3中并且基于Luc M的答案(第一个),您可以使用像这样的闭包

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

我喜欢这个,因为我不必像使用create_function()那样用引号将函数的代码引起来


1
如果您使用的是匿名函数,则最好直接使用捕获的闭包变量,而不要使用这些objTemp东西
user102008

1
PHP5.3.3中存在一个导致崩溃的错误-bugs.php.net/bug.php?id=52719
crazyphoton 2012年

2

使用高阶函数(注意:我正在使用内联匿名函数,该函数出现在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()
    );
}

1

一种基于chaos提交的先前示例函数的新方法,该方法修复了在多数组中覆盖字符串键的错误:

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

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

1

您可以尝试以下方法:

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

1

如果您对松散数组键没事,可以使用递归闭包作为利用array_values()的回调,将多维数组展平,并确保此回调是array_walk()的参数,如下所示。

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

前面的示例的一个缺点是,与下面的使用array_walk_recursive()和简化的回调的解决方案相比,它需要编写更多的代码:

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

查看实时代码

该示例似乎比上一个示例更好,它隐藏了有关如何从多维数组中提取值的详细信息。当然会发生迭代,但是无论它需要递归还是控制结构,您都只能通过细读array.c来了解。由于函数式编程专注于输入和输出,而不是获得结果的细节,因此可以肯定的是,幕后迭代的发生方式将是令人担忧的,即直到有观点的雇主提出这样的问题为止。


1

我找到了一种将多级数组转换为一个的简单方法。我使用函数“ http_build_query”将数组转换为url字符串。然后,使用爆炸分割字符串并解码该值。

这是一个样本。

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

1

对不起,我很抱歉,但提供的答案都没有我直觉上理解的“扁平化多维数组”。即这种情况:

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

提供的所有解决方案都可以将其压平为 ['value'],但是会丢失有关键和深度的信息,另外,如果您在其他位置有另一个“ b”键,它将覆盖它们。

我想要得到这样的结果:

[
  'a_b' => 'value',
]

array_walk_recursive 不会传递有关当前正在递归的密钥的信息,因此我只是通过普通递归来做到这一点:

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

根据自己的喜好修改$ prefix和'_'分隔符。

游乐场在这里:https : //3v4l.org/0B8hf



0

简单的方法..通过递归查看。

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

?>

为什么static要为此任务使用潜在的坏主意?意外的数据保留。如果程序员不知道/不期望这种行为,这肯定会让程序员感到惊讶。 看这个示范
mickmackusa

您已经发布了“仅代码”答案-这些在StackOverflow上的价值很低,因为它们无法教育OP和未来的研究人员。请花一点时间来改善此答案,包括您的答案如何工作以及为什么您觉得它比以前的答案更好。
mickmackusa

0

有人可能会觉得这很有用,我在某个维度上将数组展平时遇到问题,因此我将其称为最后一个维度,例如,如果我有如下数组:

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

要么:

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

对于这两个数组,当我调用下面的方法时,都会得到结果:

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

因此,我将扁平化为应该保持不变的最后一个数组维,可以将以下方法重构为实际上在任何级别上停止:

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

-1

如果您只对某个特定键的值感兴趣,则可能会发现此方法很有用:

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

例:

给定$ 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
  }
}

$variables['role_action_list']=valuelist($get_role_action, 'ACTION_CD');会导致:

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

在这里,您可以执行值查询,如下所示:

if( in_array('ADD_DOCUMENT', $variables['role_action_list']) ){
    //do something
};

这是同名的CFML函数的PHP替代品。
Jeromy French

我之所以投票,是因为它是对错误问题的正确答案。
mickmackusa

-1

这些都不对我有用...所以必须自己运行。正常工作:

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

此仅代码的答案无法按需工作。 3v4l.org/U3bfp <-证明这是我拒绝投票的原因。
mickmackusa

-1

给定多维数组并将其转换为一维,可以通过取消所有具有数组的值并将其保存到第一维来完成,例如:

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

我不赞成这个答案,因为它不适用于任何版本。3v4l.org/7cO9N(Proof)另外,each()自php7.2起已弃用。
mickmackusa
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.