【问题标题】:PHP parent/child iteration in single-dimension array [duplicate]单维数组中的PHP父/子迭代[重复]
【发布时间】:2018-02-17 18:24:21
【问题描述】:

对于如何解决这个问题我有点茫然,我怀疑foreach 不是正确答案,我知道array_walk()RecursiveArrayIterator 的存在,但我没有使用任何一个的真实世界的经验,所以我可以用一些指向正确方向的指针来做。 (如果对答案有任何影响,我正在使用 PHP 7.1.9)。

源数据

我有一个单维数组,其中包含对象的父/子树。您可以假设树具有未知且可变的嵌套深度。一个基本示例如下所示:

$sampleParent=array("id"=>101,"level"=>1,"parent_id"=>1,"name"=>"parent","otherparam"=>"bar");
$sampleChildD1=array("id"=>234,"level"=>2,"parent_id"=>101,"name"=>"level1","otherparam"=>"bar");
$sampleChildD2=array("id"=>499,"level"=>3,"parent_id"=>234,"name"=>"level2","otherparam"=>"bar"); 
$sampleTree=array($sampleParent,$sampleChildD1,$sampleChildD2);

期望的输出

最终目标是输出 HTML 列表(即<ul><li></li></ul>),每个父列表一个列表。通过嵌套<ul> 标签实现子元素的嵌套。所以对于我上面的例子:

<ul>
<li>
<a href="#">parent</a>
</li>
    <ul>
    <li>
    <a href="#">level1</a>
        <ul>
        <li>
        <a href="#">level2</a>
        </li>
        </ul>
    </li>
    </ul>
</ul>

【问题讨论】:

  • 到目前为止你尝试过什么?一棵树如何存储在单维数组中?每个父母总是只有一个孩子吗?
  • $sampleTree 在您执行 '$sampleTree=array($sampleParent,$sampleChildD1,$sampleChildD2);' 时变成带有数组的数组或多维数组。这对您的问题没有太大帮助,以澄清在处理/解析 sampleTree 期间您不再处理单维数组。

标签: php arrays


【解决方案1】:

你可以扩展RecursiveArrayIterator:

class AdjacencyListIterator extends RecursiveArrayIterator
{
    private $adjacencyList;

    public function __construct(
        array $adjacencyList,
        array $array = null,
        $flags = 0
    ) {
        $this->adjacencyList = $adjacencyList;

        $array = !is_null($array)
            ? $array
            : array_filter($adjacencyList, function ($node) {
                return is_null($node['parent_id']);
            });

        parent::__construct($array, $flags);
    }

    private $children;

    public function hasChildren()
    {
        $children = array_filter($this->adjacencyList, function ($node) {
            return $node['parent_id'] === $this->current()['id'];
        });

        if (!empty($children)) {
            $this->children = $children;
            return true;
        }

        return false;
    }

    public function getChildren()
    {
        return new static($this->adjacencyList, $this->children);
    }
}

然后你可以用RecursiveIteratorIterator 遍历这个迭代器,或者你可以扩展前者,用HTML 半自动地装饰树:

class UlRecursiveIteratorIterator extends RecursiveIteratorIterator
{
    public function beginIteration()
    {
        echo '<ul>', PHP_EOL;
    }

    public function endIteration()
    {
        echo '</ul>', PHP_EOL;
    }

    public function beginChildren()
    {
        echo str_repeat("\t", $this->getDepth()), '<ul>', PHP_EOL;
    }

    public function endChildren()
    {
        echo str_repeat("\t", $this->getDepth()), '</ul>', PHP_EOL;
        echo str_repeat("\t", $this->getDepth()), '</li>', PHP_EOL;
    }
}

拥有这两个类,您可以像这样迭代您的树:

$iterator = new UlRecursiveIteratorIterator(
    new AdjacencyListIterator($sampleTree),
    RecursiveIteratorIterator::SELF_FIRST
);

foreach ($iterator as $leaf) {
    echo str_repeat("\t", $iterator->getDepth() + 1);
    echo '<li>', '<a href="#">', $leaf['name'], '</a>';
    echo $iterator->hasChildren() ? '' : '</li>', PHP_EOL;
}

这里是working demo

请注意,此处使用的str_repeatPHP_EOL 仅用于演示目的,应在实际代码中删除。

【讨论】:

    【解决方案2】:

    我可以建议以 OOP 方式执行此操作吗? 我会创建一个具有属性和子列表的对象。如果您愿意,您还可以添加一个从子级到其父级的链接,如本示例中所示

    class TreeNode {
      // string
      public $name;
      // integer
      public $id;
      // TreeNode
      public $parent;
      // TreeNode[]
      public $children;
    }
    

    有了这个结构,使用 foreach 迭代它应该非常简单。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-23
      • 2012-12-03
      • 2013-08-07
      • 2020-03-21
      • 2012-07-07
      • 2013-11-11
      • 2018-08-18
      相关资源
      最近更新 更多