PHP - 将多维数组转换为带有点符号键的二维数组

28

有许多提示和代码示例可以使用点表示法访问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可能能帮助我构建新的键,因为它似乎可以通过递归处理大部分繁重的工作,但它不提供所有数组键。例如,在$myArray上使用array_walk_recursive(如在PHP文档页面上运行示例函数)只会向我提供没有数组值的键。我继续尝试编写自己的递归函数,使用一些老式的foreach循环,但今天已经很长时间了,也让我头疼。我将继续努力并更新,如果我得到它(或任何更接近)。 - TheCheese
2
Laravel提供了Illuminate\Support\Arr::dot($the_array)来实现此功能,可以在php artisan tinker中进行测试。 - DevonDahon
5个回答

87

代码

$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

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


1
现在,这个函数的反函数怎么样? - Petah
1
@Petah,请查看https://dev59.com/kGkw5IYBdhLWcg3wzdtF@rambocoder,“ritit”是什么意思?我的意思是这个词...谢谢!-->啊,RecursiveITeratorITerator...没错 :) - Adrian Föder
我喜欢这个。在看到这个之前,我已经构建了一个自定义函数。虽然这个稍微慢一些,但很好地处理了没有索引的数组/子数组。不错! - Ema4rl

6

已经有了使用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方法时准备好键。 这里有一个可用的演示。

6
这将处理任意层次的嵌套:
<? //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',
            ],
        ]
    )
);
?>

这个答案包含了性能最高的代码。没有在循环中使用array_merge,而是使用了较慢的SPL。 - undefined

2

这是我对递归解决方案的理解,适用于任意深度的数组:

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)


1

这是另一种类似于上面的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/ 在这种情况下,对于1000次迭代php5.6给出的示例数组,此代码的速度是两倍快(递归=.032 vs interator=.062)- 但对于大多数情况而言,差异可能不重要。主要是因为我发现像这样简单的用例的迭代器逻辑毫无必要地复杂,所以我更喜欢递归。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接