PHP递归:展平树,保留元数据

时间:2015-10-02 17:50:50

标签: php recursion tree adjacency-list

想象一下,您有一棵树代表您想要保存到表中的族,但保留了嵌套级别。我想将下面的帖子数据放到下面的结构中。

我相信RecursiveIterator可能是这样做的方法,但我不确定如何这样做。我的代码适用于大多数情况,但它已成为一种丑陋,臃肿的东西。如果你愿意,我可以发帖。

stdClass Object
(
    [name] => Smith
    [type] => Family
    [children] => Array(
            [1] => stdClass Object
                (
                    [name] => Michael
                    [type] => Uncle
                    [children] => Array(
                            [0] => stdClass Object
                                (
                                    [name] => Jared
                                    [type] => cousin
                                )
                        )
                )
            [2] => stdClass Object
                (
                    [name] => Jeff
                    [type] => Dad
                    [children] => Array(
                            [0] => stdClass Object
                                (
                                    [name] => Jonas
                                    [type] => self
                                )
                            [1] => stdClass Object
                                (
                                    [name] => Leah
                                    [type] => sister
                                    [children] => Array(
                                            [0] => stdClass Object
                                                (
                                                    [name] => Jacob
                                                    [type] => nephew
                                                )
                                        )
                                )
                        )
                )
        )
)

持久性记录应如下所示:

Array
(
    stdClass Object ( [name] => Smith [type] => Family [subgroup] => 0 [parent_subgroup] => )
    stdClass Object ( [name] => Michael [type] => Uncle [subgroup] => 1 [parent_subgroup] => 0 )
    stdClass Object ( [name] => Jared [type] => Cousin [subgroup] => 2 [parent_subgroup] => 1 )
    stdClass Object ( [name] => Jeff [type] => Dad [subgroup] => 1 [parent_subgroup] => 0 )
    stdClass Object ( [name] => Jonas [type] => self [subgroup] => 3 [parent_subgroup] => 1 )
    stdClass Object ( [name] => Leah [type] => sister [subgroup] => 3 [parent_subgroup] => 1 )
    stdClass Object ( [name] => Jacob [type] => nephew [subgroup] => 4 [parent_subgroup] => 3 )     
)

P.S。不,我和我的妹妹没有孩子。这只是我的比喻。 ;)

1 个答案:

答案 0 :(得分:3)

RecursiveIterator课程可能会有点混乱,我想尽量保持简单。您可以使用RecursiveIteratorIterator循环遍历迭代器的值,它甚至可以为您提供当前深度(或者在您的情况下为subgroup)。

这里的挑战是父母不是一个数组,但我们可以在构造函数中处理它。

<?php
class FamilyIterator implements RecursiveIterator{
    private $data, $counter;

    public function __construct($familyTree){
        $this->data = is_array($familyTree) ? $familyTree : [$familyTree];
    }

    public function current(){
        $row = $this->data[$this->counter];
        return (object)[
            'name' => $row->name,
            'type' => $row->type
        ];
    }

    public function key(){
        return $this->counter;
    }

    public function next(){
        $this->counter++;
    }

    public function rewind(){
        $this->counter = 0;
    }

    public function valid(){
        return $this->counter < count($this->data);
    }

    public function hasChildren(){
        $row = $this->data[$this->counter];
        return isset($row->children);
    }

    public function getChildren(){
        $row = $this->data[$this->counter];
        return new self($row->children);
    }
}

然后你可以使用这个类:

$loop = new RecursiveIteratorIterator(
    new FamilyIterator($dataObj),
    RecursiveIteratorIterator::SELF_FIRST
);

当您foreach超过$loop时,它会在需要时自动调用getChildren方法,因此在foreach中您将分别拥有RecursiveIteratorIterator行。您甚至可以向$newData = []; foreach($loop as $row){ $row->subgroup = $loop->getDepth(); $newData[] = $row; } 询问深度。

RecursiveIterator

DEMO:https://eval.in/444078

这可能不是完全您想要的,但希望它能指出您正确的方向。 (defun endless/visit-pull-request-url () "Visit the current branch's PR on Github." (interactive) (let ((repo (magit-get "remote" (magit-get-remote) "url"))) (if (string-match "github\\.com" repo) (visit-gh-pull-request repo) (visit-bb-pull-request repo)))) (defun visit-gh-pull-request (repo) "Visit the current branch's PR on Github." (interactive) (browse-url (format "https://github.com/%s/pull/new/%s" (replace-regexp-in-string "\\`.+github\\.com:\\(.+\\)\\.git\\'" "\\1" repo) (cdr (magit-get-remote-branch))))) ;; Bitbucket pull requests are kinda funky, it seems to try to just do the ;; right thing, so there's no branches to include. ;; https://bitbucket.org/<username>/<project>/pull-request/new (defun visit-bb-pull-request (repo) (browse-url (format "https://bitbucket.org/%s/pull-request/new" (replace-regexp-in-string "\\`.+bitbucket\\.org:\\(.+\\)\\.git\\'" "\\1" repo)))) ;; visit PR for github or bitbucket repositories with "v" (eval-after-load 'magit '(define-key magit-mode-map "v" #'endless/visit-pull-request-url)) 不需要复杂。