【问题标题】:PHP - Convert multidimensional array to 2D array with dot notation keysPHP - 使用点符号键将多维数组转换为二维数组
【发布时间】:2017-03-04 13:35:50
【问题描述】:

有很多使用点符号访问 PHP 数组的技巧和代码示例,但我想做一些相反的事情。我想采用这样的多维数组:

$myArray = array(
    'key1' => 'value1',
    'key2' => array(
        'subkey' => 'subkeyval'
    ),
    'key3' => 'value3',
    'key4' => array(
        'subkey4' => array(
            'subsubkey4' => 'subsubkeyval4',
            'subsubkey5' => 'subsubkeyval5',
        ),
        'subkey5' => 'subkeyval5'
    )
);

然后把它变成这个(可能通过一些递归函数):

$newArray = array(
    'key1'                    => 'value1',
    'key2.subkey'             => 'subkeyval',
    'key3'                    => 'value3',
    'key4.subkey4.subsubkey4' => 'subsubkeyval4',
    'key4.subkey5.subsubkey5' => 'subsubkeyval5',
    'key4.subkey5'            => 'subkeyval5'
);

【问题讨论】:

  • 我认为 array_walk_recursive 可能能够帮助我构建新密钥,因为它似乎可以通过递归完成很多繁重的工作,但它不提供 all 数组的键。例如,在 $myArray 上使用 array_walk_recursive(通过 PHP 文档页面上的示例函数运行)只会为我提供没有数组值的键。我正在继续尝试使用一些旧的 foreach 循环编写我自己的递归函数,但这是漫长的一天,让我很头疼。如果我得到它(或更接近),我会继续努力并更新
  • Laravel 有Illuminate\Support\Arr::dot($the_array) 做,可以在php artisan tinker 测试。

标签: php arrays


【解决方案1】:

密码

$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($myArray));
$result = array();
foreach ($ritit as $leafValue) {
    $keys = array();
    foreach (range(0, $ritit->getDepth()) as $depth) {
        $keys[] = $ritit->getSubIterator($depth)->key();
    }
    $result[ join('.', $keys) ] = $leafValue;
}

输出

Array
(
    [key1] => value1
    [key2.subkey] => subkeyval
    [key3] => value3
    [key4.subkey4.subsubkey4] => subsubkeyval4
    [key4.subkey4.subsubkey5] => subsubkeyval5
    [key4.subkey5] => subkeyval5
)

演示:http://codepad.org/YiygqxTM

我得走了,但如果你明天需要解释,可以问我。

【讨论】:

  • 这是一个比我想象的要好 1000% 的答案!我以前根本没有玩过 RecursiveIteratorIterator(但我现在会玩)所以我什至没有想到它。做得很好!
  • 现在,这个函数的反转怎么样?
  • @Petah,见stackoverflow.com/q/9635968/1388892@rambocoder,什么是“ritit”?我的意思是这个词......谢谢! --> 啊,RecursiveITeratorITerator... 没错 :)
  • 我喜欢这个。在我看到这个之前构建了一个自定义函数。这稍微慢一些,但可以很好地处理无索引数组/子数组。不错!
【解决方案2】:

这将处理任意级别的嵌套:

<? //PHP 5.4+
$dotFlatten = static function(array $item, $context = '') use (&$dotFlatten){
    $retval = [];
    foreach($item as $key => $value){
        if (\is_array($value) === true){
            foreach($dotFlatten($value, "$context$key.") as $iKey => $iValue){
                $retval[$iKey] = $iValue;
            }
        } else {
            $retval["$context$key"] = $value;
        }
    }
    return $retval;
};

var_dump(
    $dotFlatten(
        [
            'key1' => 'value1',
            'key2' => [
                'subkey' => 'subkeyval',
            ],
            'key3' => 'value3',
            'key4' => [
                'subkey4' => [
                    'subsubkey4' => 'subsubkeyval4',
                    'subsubkey5' => 'subsubkeyval5',
                ],
                'subkey5' => 'subkeyval5',
            ],
        ]
    )
);
?>

【讨论】:

    【解决方案3】:

    RecursiveIteratorIterator 已经有了答案。但这里有一个更优化的解决方案,它避免使用嵌套循环

    $iterator = new RecursiveIteratorIterator(
        new RecursiveArrayIterator($arr),
        RecursiveIteratorIterator::SELF_FIRST
    );
    $path = [];
    $flatArray = [];
    
    foreach ($iterator as $key => $value) {
        $path[$iterator->getDepth()] = $key;
    
        if (!is_array($value)) {
            $flatArray[
                implode('.', array_slice($path, 0, $iterator->getDepth() + 1))
            ] = $value;
        }
    }
    

    这里有几点需要说明。注意这里使用了RecursiveIteratorIterator::SELF_FIRST 常量。这很重要,因为默认值是RecursiveIteratorIterator::LEAVES_ONLY,它不会让我们访问所有密钥。所以有了这个常数集,我们从数组的顶层开始,往更深处走。这种方法可以让我们存储密钥的历史,并在我们使用RecursiveIteratorIterator::getDepth 方法丰富叶子时准备密钥。

    Here is a working demo.

    【讨论】:

      【解决方案4】:

      这是我对递归解决方案的看法,它适用于任何深度的数组:

      function convertArray($arr, $narr = array(), $nkey = '') {
          foreach ($arr as $key => $value) {
              if (is_array($value)) {
                  $narr = array_merge($narr, convertArray($value, $narr, $nkey . $key . '.'));
              } else {
                  $narr[$nkey . $key] = $value;
              }
          }
      
          return $narr;
      }
      

      可以称为$newArray = convertArray($myArray)

      【讨论】:

        【解决方案5】:

        这是另一种类似于上述 Blafrat 的方法 - 但只是将数组作为值处理。

         function dot_flatten($input_arr, $return_arr = array(), $prev_key = '')
         {
             foreach ($input_arr as $key => $value)
             {
                $new_key = $prev_key . $key;
        
                // check if it's associative array 99% good
                if (is_array($value) && key($value) !==0 && key($value) !==null)
                {
                    $return_arr = array_merge($return_arr, dot_flatten($value, $return_arr, $new_key . '.'));
                }
                else
                {
                    $return_arr[$new_key] = $value;
                }
            }
        
            return $return_arr;
        }
        

        (唯一无法捕获的情况是您有一个关联值,但第一个键是 0。)

        请注意,RecursiveIteratorIterator 可能比常规递归函数慢。 https://xenforo.com/community/threads/php-spl-why-is-recursiveiteratoriterator-100x-slower-than-recursive-search.57572/

        在这种情况下,使用为 php5.6 提供的 1000 次迭代的示例数组,这段代码的速度是两倍(recursive=.032 vs interator=.062) - 但在大多数情况下差异可能微不足道。主要是我更喜欢递归,因为对于像这样的简单用例,我发现迭代器的逻辑不必要地复杂。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-01-23
          • 2012-08-15
          • 1970-01-01
          • 2014-01-22
          • 1970-01-01
          • 2023-04-03
          • 2019-07-09
          相关资源
          最近更新 更多