PHP在连接键时将嵌套数组转换为单个数组?

Posted

技术标签:

【中文标题】PHP在连接键时将嵌套数组转换为单个数组?【英文标题】:PHP convert nested array to single array while concatenating keys? 【发布时间】: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'
             )

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

【问题讨论】:

【参考方案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://***.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 这种技术不可能为这个问题提供所需的输出。换句话说,这是对另一个问题的正确答案。

以上是关于PHP在连接键时将嵌套数组转换为单个数组?的主要内容,如果未能解决你的问题,请参考以下文章

将嵌套的 JSONB 数组连接成单个字符串

如何在 Swift 中将单个数组转换为嵌套数组? [复制]

当字符串包含 PHP 中的重复键时,如何将字符串转换为关联数组? [复制]

PHP将单维数组转换为嵌套数组

php,json_encode,嵌套数组,带有一个“左连接”查询

带有连接的 SQL 查询以获取嵌套的对象数组