【问题标题】:PHP convert nested array to single array while concatenating keys?PHP在连接键时将嵌套数组转换为单个数组?
【发布时间】:2010-12-25 08:52:47
【问题描述】:

这是一个示例数组:

 $foo = array(
           'employer' => array(
                    'name' => 'Foobar Inc',
                    'phone' => '555-555-5555'
                     ),
           'employee' => array(
                    'name' => 'John Doe',
                    'phone' => '555-555-5556',
                    'address' => array(
                           'state' => 'California',
                           'zip' => '90210'
                        )
                    ),
           'modified' => '2009-12-01',
         );

我想得到这样的结果:

$fooCompressed = array(
             'employer_name' => 'Foobar Inc',
             'employer_phone' => '555-555-5555',
             'employee_name' => 'John Doe',
             'employee_phone' => '555-555-5556'
             'employee_address_state' => 'California',
             'employee_address_zip' => '90210',
             'modified' => '2009-12-01'
             )

我将如何编写一个递归函数来处理这个问题?

【问题讨论】:

    标签: php arrays recursion multidimensional-array


    【解决方案1】:

    类似这样的:

    function makeNonNestedRecursive(array &$out, $key, array $in){
        foreach($in as $k=>$v){
            if(is_array($v)){
                makeNonNestedRecursive($out, $key . $k . '_', $v);
            }else{
                $out[$key . $k] = $v;
            }
        }
    }
    
    function makeNonNested(array $in){
        $out = array();
        makeNonNestedRecursive($out, '', $in);
        return $out;
    }
    
    // Example
    $fooCompressed = makeNonNested($foo);
    

    【讨论】:

    • +1 这与我所做的非常接近。因为键正在被修改,所以没有内置函数可以为你做这件事,你肯定需要递归来深入了解任何也是数组的子值。
    【解决方案2】:

    我认为这个使用 http_build_query 的“技巧”在没有递归的情况下不那么令人眼花缭乱(或者至少让 php 为你做这件事)

    如果您的 str_replace 使用 [ 和 ] 的 url 编码值,则为 3 行代码

    $string      = http_build_query($array);
    $string      = urldecode($string);
    $string      = str_replace(
                        array('[',']'),
                        array('_','') , 
                        $string
                    );
    parse_str($string, $flat_array);
    

    $flat_array 变为:

    array(7) {
      ["employer_name"]         =>"Foobar Inc"
      ["employer_phone"]        =>"555-555-5555"
      ["employee_name"]         =>"John Doe"
      ["employee_phone"]        =>"555-555-5556"
      ["employee_address_state"]=>"California"
      ["employee_address_zip"]  =>"90210"
      ["modified"]              =>"2009-12-01"
    }
    

    【讨论】:

      【解决方案3】:

      这是一个允许您通过第二个参数指定顶级前缀的函数:

      function flatten_array($array, $prefix = null) {
        if ($prefix) $prefix .= '_';
      
        $items = array();
      
        foreach ($array as $key => $value) {
          if (is_array($value))
            $items = array_merge($items,  flatten_array($value, $prefix . $key));
          else
            $items[$prefix . $key] = $value;
        }
      
        return $items;
      }
      

      【讨论】:

        【解决方案4】:

        我更喜欢的方法与此处发布的一些方法非常相似,但并不相同。我在一个受骗的帖子中发现了它:https://stackoverflow.com/a/9546215/4791386 用户“Felix Kling”

        他的代码将数组键展平,生成带有点连接键的一维数组,这意味着数值数组将创建自己的“键路径”。这非常有用,但是数组中的大量相似项可能会导致大量无意义的相似路径。

        function flatten($array, $prefix = '') {
            $result = array();
            foreach($array as $key=>$value) {
                if(is_array($value)) {
                    $result = $result + flatten($value, $prefix . $key . '.');
                }
                else {
                    $result[$prefix . $key] = $value;
                }
            }
            return $result;
        }
        

        在我的例子中,我还需要一个“独特的”路径作为数组键,以及我可以指定的数据样本。所以我扩展了他的方法,添加了一个数字键压缩可选参数。还添加了可选的参数分隔符配置。

        主要目的是便于分析关键结构和路径相关数据。我认为当预期任务是键映射以进行进一步的完整数据操作时,此方法很有用。

        /**
        * Convert a multidimensional array into a single dimension array.
        * Nested array keys will be concatenated with the $separator string
        * Numeric keys can also be flattened in a "unique key" array style with $numeric_squash 
        * If $numeric_squash is true, numeric array keys are concatenated with $numeric_squash_separator, 
        * for later detection and processing if necessary. "[*]" by default.
        * If $numeric_squash_separator is set to false, the array key is flattened so that the values 
        * would be displayed as if there were no numeric array.
        *
        * array  $array                    : Array to be flattened
        * string $prefix                   : String to prepend on flattened keys
        * string $separator                : String concatenated between nested array keys.
        * bool   $numeric_squash           : Squash numeric array keys
        * string $numeric_squash_separator : String replacing numeric keys, none if false
        
        */  
        
        public static function array_flatten($array, $prefix = '', $separator = '.' , $numeric_squash = false , $numeric_squash_separator = '[*]') {
            $result = array();
            foreach($array as $key => $value) {
                if(is_array($value)) {
                    if($numeric_squash && is_numeric($key))
                        $n_key = $numeric_squash_separator ? $numeric_squash_separator . $separator: '';
                    else
                        $n_key = $key . $separator;
        
                    $result = $result + self::array_flatten($value, $prefix . $n_key  , $separator , $numeric_squash , $numeric_squash_separator);
                }
                else {
                    $result[$prefix . ($numeric_squash && is_numeric($key) ? '' : $key)] = $value;
                }
            }
            return $result;
        }
        

        另外说这个函数没有性能优化,迭代可以保存在 numeric_squash 上,也可以保存一些比较操作。

        【讨论】:

          【解决方案5】:

          只有 array_* php 函数 + 递归的解决方案:

          <?php
          $array = array(
              "level1"=>"value",
              "level2" => ["level11" => "value", "level21" => "value"],
              "level3" => ["level2" => ["level1" => "value"]],
              "level4" => ["level3" => ["level2" => ["level1" => "value"]]],
              "level5" => ["level4" => ["level3" => ["level2" => ["level1" => "value"]]]],
          );
          class GharbiFlat {
           
              /**
               * flatten array with combined keys
               */
              public function arrayFlat($array, $keySeparator = '_')
              {
                  $result = [];
                  array_walk(
                      $array,
                      function ($v, $pk) use (&$result, $keySeparator) {
                          if (is_array($v)) {
                              $result += $this->arrayFlat(
                                  array_combine(
                                      array_map(
                                          function ($k) use ($pk, $keySeparator) {
                                              return $pk . $keySeparator . $k;
                                          },
                                          array_keys($v)
                                      ),
                                      $v
                                  ),
                                  $keySeparator
                              );
                          } else {
                              $result[$pk] = $v;
                          }
                      }
                  );
                  return $result;
              }
          }
          
          $example = new GharbiFlat();
          
          print_r($example->arrayFlat($array));
          

          输出:

          Array
          (
              [level1] => value
              [level2_level11] => value
              [level2_level21] => value
              [level3_level2_level1] => value
              [level4_level3_level2_level1] => value
              [level5_level4_level3_level2_level1] => value
          )
          

          【讨论】:

            【解决方案6】:
            /**
             * Flatten a multi-dimensional array or a nested object, constructing concatenated keys for
             *    nested elements.
             * @param array or object $array - the array or object to be flattened
             * @param array or string $key_path - current parent keys path.
             *    Pass this parameter as string if you need to set a common prefix for all keys 
             * @param string $level_separator - keys concatenation glue
             * @param array $flat - resulting flattened array (omit this parameter when calling the function)
             * @return single-dimensional array with all array keys as concatenated keys of elements' 
             *    paths through the data structure
             */
             function flattenArray($array, &$key_path = array(), $level_separator = '.', &$flat = array())
             {
                  if(!is_array($key_path))
                  {
                       // sanitize key_path
                       $key_path = array((string)$key_path);
                   }
                   foreach($array as $key => $value)
                   {
                        // push current key to path
                        array_push($key_path, $key);
            
                        if(is_array($value) || is_object($value))
                        {
                             // next level recursion
                             $flat = array_merge($flat, flattenArray($value, $key_path, $level_separator, $flat));
                         }
                         else
                         {
                              // write the value directly
                              $flat[implode($level_separator, $key_path)] = $value;
                          }
            
                          // remove used key
                          array_pop($key_path);
                    }
            
                    return $flat;
              }
            

            【讨论】:

              【解决方案7】:

              经过几次迭代,我已经能够针对这个问题改进一个解决方案,该解决方案使用基于堆栈的方法来避免递归,从而稍微简化了一些事情。

              /***
               * @name array_flatten
               * @author Tom Penzer @tpenzer
               * Flattens a multi-tiered array into a single-tiered 
               * associative array with keys reflective of their 
               * values' hierarchy.
               *
               * @param    array    $array       Required - the multi- 
               * level keyed array to be flattened
               * @param    string   $separator   Optional - the string 
               * used to separate the keys from different levels of 
               * the hierarchy
               *
               * @return   array    a single-level keyed array
               ***/
              function array_flatten($array, $separator = '_') {
                  $output = array();
              
                  while (list($key, $value) = each($array)) {
                      if (is_array($value)) {
                          $build = array();
                          foreach ($value as $s_key => $s_value) {
                              $build[$key . $separator . $s_key] = $s_value;
                          }
                          unset($array[$key]);
                          $array = $build + $array;
                          unset($build);
                          continue;//skip write to $output
                      }
                      $output[$key] = $value;
                      unset($array[$key]);
                  }
              
                  return $output;
              }
              

              不完全是请求的方法,但它与解决问题的递归方法形成了很好的对比。

              【讨论】:

                【解决方案8】:

                这将展平一个多维关联数组,如果它是重复的,则将一个数字附加到键上。 如果您不介意使用数字索引来区分重复键而不是连接键,这可能是一个解决方案。

                $result = array();
                array_walk_recursive($your_array, function($v, $k) use (&$result){ $i = ""; for (; isset($result[$k."$i"]); $i++); $result[$k."$i"] = $v; });
                

                我怀疑它可以进一步处理连接键。

                上面的解决方案基本上是为了做这种事情

                <?php
                    $xml_str = "
                    <images>
                        <image>
                            <position>0</position>                  
                        </image>
                        <image1>
                            <position>10</position>
                        </image1>
                    </images>";
                                    // turn the xml into a multidimentional array
                            $ob = simplexml_load_string($xml_str);
                            $json = json_encode($ob);
                            $my_array = json_decode($json, true);
                
                            print_r($my_array);
                                    // flatten it
                            $result = array();
                            array_walk_recursive($my_array, function($v, $k) use (&$result){ $i = ""; for (; isset($result[$k."$i"]); $i++); $result[$k."$i"] = $v; });
                
                            print_r($result);
                ?>
                

                【讨论】:

                • array_walk_recursive() 忽略所有非叶节点。 3v4l.org/Ca4m2 这种技术不可能为这个问题提供所需的输出。换句话说,这是对另一个问题的正确答案。
                猜你喜欢
                • 1970-01-01
                • 2011-10-30
                • 2018-05-15
                • 1970-01-01
                • 2013-07-26
                • 1970-01-01
                • 2015-05-17
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多