为递归数组中的每个元素设置id


set id for each element in recursive array

我想为每个元素和子元素以及子元素的子元素设置一个id或索引,依此类推

<?php
    $data = array(
        array(
            'title' => 'a',
            'children' => array(
                array(
                    'title' => 'a-a',
                    'children' => array(
                        array(
                            'title' => 'a-a-a',
                            'children' => array(
                            )
                        )                
                    )
                ),
                array(
                    'title' => 'a-b',
                    'children' => array(
                    )
                )                        
            )
        ),
        array(
            'title' => 'b',
            'children' => array(
            )
        )
    );
?>

我正在寻找与递归一起工作的php代码或函数,以添加索引键和数字(desc),类似于以下输出

<?php
    $data = array(
        array(
            'index' => 1,
            'children' => array(
                array(
                    'index' => 2,
                    'children' => array(
                        array(
                            'index' => 3,
                            'children' => array(
                            )
                        )                
                    )
                ),
                array(
                    'index' => 4,
                    'children' => array(
                    )
                )                        
            )
        ),
        array(
            'index' => 5,
            'children' => array(
            )
        )
    );
?>

从前有一项研究得出结论,一个年轻的学徒程序员在寻求成为绝地高级程序员的过程中面临的最大挑战是:

[…]有三个主要的语义障碍会让新手命令式程序员感到困惑。按顺序为:

  • 分配和顺序
  • 递归/迭代
  • 并发

我第一次遇到这个理论是在Jeff Atwood的博客文章《将编程绵羊与非编程山羊分离》中,该文章基于一篇科学论文:骆驼有两个驼峰(工作标题)

这是实现您想要的代码(减去echo调用,这些调用只会使可视化步骤变得更容易):

function addIndexRecursive(&$data) {
    static $index = 0;
    echo "The index is: $index" . PHP_EOL;
    foreach ($data as &$item) {
        $index++;
        echo "The index was incremented: {$index}" . PHP_EOL;
        $item = array('index' => $index) + $item;
        echo "Add the index to the item with title: {$item['title']}" . PHP_EOL;
        if (count($item['children']) > 0) {
            echo "This item has children so call the index function" . PHP_EOL;
            addIndexRecursive($item['children'], $index);
        // this else branch is only for illustration  purposes only
        } else {
            echo "There are no children therefore I stop" . PHP_EOL;
        }
    }
}

所以让我们仔细分析一下。

首先要注意的是:我通过引用传递数组。这只是品味的问题,我更喜欢直接修改数组,而不是构建另一个副本。

static $index将帮助我跟踪当前索引,并将随着遇到的每一项而递增。让我们看看这个static有什么样的魔法(根据可变范围):

静态变量只存在于局部函数作用域中,但当程序执行离开该作用域时,它不会丢失其值。

foreach ($data as &$item)我再次希望元素通过引用传递,这样我就可以在循环中直接修改它们。

现在我们讨论了实现细节,让我们看看函数的回溯是什么:

addIndexRecursive was call and the index is: 0
The index was incremented: 1
Add the index to the item with title: a
This item has children so call the function
    addIndexRecursive was call and the index is: 1
    The index was incremented: 2
    Add the index to the item with title: a-a
    This item has children so call the function
        addIndexRecursive was call and the index is: 2
        The index was incremented: 3
        Add the index to the item with title: a-a-a
        There are no children therefore I stop
    The index was incremented: 4
    Add the index to the item with title: a-b
    There are no children therefore I stop
The index was incremented: 5
Add the index to the item with title: b
There are no children therefore I stop

你会不断遇到这类问题,所以你越早掌握递归概念越好。

这是"嵌套数组"的一个例子,其中单个数组需要:

1) an entry to be added: 'index' => <incrementing number> where there is a 'children' entry in the array.
2) an entry to be removed where: 'title' exists in the array.

由于数组可以"嵌套",因此"递归"是处理"数据"数组的最合适方式。

我发现修改"就地"数组比构建"输出"数组更容易。因此,"数组引用"(&$arrayEntry)被大量使用。

我使用提供的测试数据,并测试"必需"数组和"索引"数组在"结构"方面是否相等。

这是经过测试的代码:Windows XP上的PHP 5.3.18工作代码:viper-7.com

完成工作的功能:

function addChildrenIndex(&$destData, &$idxNew)
{
    foreach($destData as $currentKey => &$currentValue) { // pass be reference so i can amend it
        if (is_array($currentValue)) { // ensure is 'children' array
            if (isset($currentValue['children'])) { // add the 'index' entry
                $currentValue['index'] = $idxNew;
                $idxNew++;
            }
            if (isset($currentValue['title'])) { // remove the 'title' entry!
                unset($currentValue['title']);
            }
            // as it is an array we 'recurse' around it - always...
            addChildrenIndex($currentValue, $idxNew);
        }
    }
}

驾驶代码:

$idxNew = 1;  // starting index.
// COPY the source data so i can test it later if required...
$indexedData = $data; // output array
addChildrenIndex($indexedData, $idxNew); // re-index the array 'in place'
// echo 'Required Array: <pre>';
// print_r($requiredData);
// echo '</pre>';
// echo 'OutputArray: <pre>';
// print_r($indexedData);
// echo '</pre>';
// test array structures to contain same entries...
var_dump($indexedData == $requiredData, 'ARE EQUAL?');

所需测试数据:见问题

索引输出:

Array
(
    [0] => Array
        (
            [children] => Array
                (
                    [0] => Array
                        (
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [children] => Array
                                                (
                                                )
                                            [index] => 3
                                        )
                                )
                            [index] => 2
                        )
                    [1] => Array
                        (
                            [children] => Array
                                (
                                )
                            [index] => 4
                        )
                )
            [index] => 1
        )
    [1] => Array
        (
            [children] => Array
                (
                )
            [index] => 5
        )
)