【问题标题】:How to "flatten" a multi-dimensional array to simple one in PHP? [duplicate]如何在 PHP 中将多维数组“展平”为简单数组? [复制]
【发布时间】:2010-10-06 07:24:03
【问题描述】:

这可能是初学者的问题,但我已经阅读了较长时间的文档,但找不到任何解决方案。我想我可以对每个维度使用 implode,然后将这些字符串与 str_split 重新组合在一起以创建新的简单数组。但是我永远不知道连接模式是否也在值中,所以在执行str_split 之后,我的原始值可能会中断。

对于多维数组内部的数组,是否有类似combine($array1, $array2) 的东西?

【问题讨论】:

标签: php arrays multidimensional-array


【解决方案1】:

对于 necrobumping 感到抱歉,但所提供的答案都没有达到我直观理解为“扁平化多维数组”的效果。即本案:

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      这是一个示例。

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

      【讨论】:

        【解决方案4】:

        如果您特别有一个不超过一级深度的数组(我认为常见的用例),您可以使用 array_merge 和 splat 运算符。

        <?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 ) );
        【解决方案5】:

        这是一条线,超级好用:

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

        在匿名函数/闭包内部很容易理解。 $v 是您的 $original_array 的值。

        【讨论】:

        • 这是唯一一个对我有用的两级数组。
        【解决方案6】:

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

        使用 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);函数定义正确定义为按引用传递。但除非我在通话期间通过引用传递,否则不起作用。
        • @jskilski 对象(在本例中为$objTmp)通过引用自动传递;数组不是。尝试使用匿名函数 (php.net/manual/en/functions.anonymous.php) 而不是 create_function
        • 由于 array_walk_recursive 中的错误,这在 php 5.3.3 中不起作用 - bugs.php.net/bug.php?id=52719
        • @crazyphoton 扭结还说This bug has been fixed in SVN.
        • 为什么这个答案提到使用array_values()?我根本看不到答案中涉及该功能的任何用途。
        【解决方案7】:
        $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);
        

        【讨论】:

        • 这个答案比接受的答案快得多。
        • 从 php5.3 开始,您现在可以使用 splat 运算符:$result = array_merge(...$array);php.net/manual/en/…
        • 您的第一个答案不适用于多维数组。 3v4l.org/tY8vD
        【解决方案8】:

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

        <?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 可能是个坏主意?意外的数据保留。如果他们不知道/不期望这种行为,这肯定会让程序员大吃一惊。 Look at this demonstration.
        • 您发布了一个“仅代码”的答案——这些在 StackOverflow 上的价值很低,因为它们无法教育 OP 和未来的研究人员。请花一点时间来改进这个答案,包括您的答案是如何工作的,以及为什么您认为它比之前的答案更好。
        【解决方案9】:

        如果您对丢失数组键没问题,您可以使用递归闭包作为利用 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);
        

        live code

        这个例子似乎比上一个更可取,隐藏了如何从多维数组中提取值的细节。当然,迭代会发生,但它是否需要递归或控制结构,只有通过阅读 array.c 才能知道。由于函数式编程关注的是输入和输出,而不是获得结果的细节,因此人们当然可以不关心幕后的迭代是如何发生的,直到有远见的雇主提出这样的问题。

        【讨论】:

          【解决方案10】:

          您可以使用Non-standard PHP library (NSPL) 中的flatten 函数。它适用于数组和任何可迭代的数据结构。

          assert([1, 2, 3, 4, 5, 6, 7, 8, 9] === flatten([[1, [2, [3]]], [[[4, 5, 6]]], 7, 8, [9]]));
          

          【讨论】:

            【解决方案11】:

            使用高阶函数(注意:我使用的是inline anonymous functions,它出现在 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()
                );
            }
            

            【讨论】:

              【解决方案12】:

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

              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(证明)另外,each() 已从 php7.2 中弃用。
              【解决方案13】:

              这些对我都不起作用... 所以不得不自己运行它。 工作得很好:

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

              【讨论】:

              【解决方案14】:

              在 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

              【讨论】:

                【解决方案15】:

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

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

                【讨论】:

                • 这是一个 PHP 仿制的同名 CFML 函数。
                • 我投了反对票,因为这是对错误问题的正确答案。
                【解决方案16】:

                你可以试试这个:

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

                【讨论】:

                  【解决方案17】:
                  /*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;
                  }
                  

                  【讨论】:

                    【解决方案18】:

                    基于上一个chaos提交的示例函数的新方法,修复了multiarrays中覆盖字符串键的bug:

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

                    【讨论】:

                      【解决方案19】:
                      // $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

                      【讨论】:

                      • 注意:仅用于基元数组。 “RecursiveArrayIterator 将所有对象视为有孩子,并尝试递归到它们中。” php.net/manual/en/class.recursivearrayiterator.php#106519
                      • @hakre: +1 同意:在这个答案中添加iterator_to_array() 将否定foreach 循环的需要。它可以是一个简单的单行函数。 (虽然有点长的单行)
                      • 我知道这是旧的但仍然有用,但是 $k 需要替换为独特的东西,例如计数器。如果内部数组中的名称与主数组中的名称相同,则仅使用 $k 会导致元素被删除。
                      【解决方案20】:

                      在 PHP>=5.3 中,根据 Luc M 的回答(第一个),您可以使用这样的闭包

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

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

                      【讨论】:

                      • 如果你使用匿名函数,你还不如直接使用捕获的闭包变量而不是objTemp这个东西
                      • PHP5.3.3 中有一个错误会导致崩溃 - bugs.php.net/bug.php?id=52719
                      【解决方案21】:

                      来自 PHP 的 user comments(简体)和 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;
                      }
                      

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

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

                      【讨论】:

                      【解决方案22】:
                      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;
                      }
                      

                      【讨论】:

                      • 抱歉,它似乎无法正确处理多维数组 - Demo
                      【解决方案23】:

                      非递归解决方案(但破坏顺序):

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

                      【讨论】:

                        猜你喜欢
                        • 2015-12-31
                        • 1970-01-01
                        • 2011-07-26
                        • 1970-01-01
                        • 2018-03-31
                        • 2020-06-06
                        • 1970-01-01
                        相关资源
                        最近更新 更多