TreeWalker::traverseDepthFirstRecursive()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 18
ccs 11
cts 11
cp 1
rs 9.6111
cc 5
nc 7
nop 3
crap 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Smoren\TreeTools;
6
7
use ArrayAccess;
8
use Generator;
9
use Smoren\TypeTools\MapAccess;
10
11
/**
12
 * @phpstan-type DictAccess = array<string, mixed>|ArrayAccess<string, mixed>|object
13
 */
14
class TreeWalker
15
{
16
    /**
17
     * Iterates a tree like a flat collection using depth-first traversal.
18
     *
19
     * If $childrenContainerKey is not null looks for children items using by this key only.
20
     *
21
     * Otherwise, considers any subarray to contain children.
22
     *
23
     * @param iterable<DictAccess> $data
24
     * @param ?string $childrenContainerKey
25
     *
26
     * @return Generator
27
     */
28 40
    public static function traverseDepthFirst(iterable $data, ?string $childrenContainerKey = null): Generator
29
    {
30 40
        yield from static::traverseDepthFirstRecursive($data, $childrenContainerKey);
31
    }
32
33
    /**
34
     * Iterates a tree like a flat collection using breadth-first traversal.
35
     *
36
     * If $childrenContainerKey is not null looks for children items using by this key only.
37
     *
38
     * Otherwise, considers any subarray to contain children.
39
     *
40
     * @param iterable<DictAccess> $data
41
     * @param ?string $childrenContainerKey
42
     *
43
     * @return Generator
44
     */
45 40
    public static function traverseBreadthFirst(iterable $data, ?string $childrenContainerKey = null): Generator
46
    {
47 40
        $level = 0;
48
        do {
49 40
            $subLevelContainer = [];
50 40
            foreach($data as $datum) {
51 32
                if($childrenContainerKey !== null) {
52 20
                    yield $level => $datum;
53 20
                    $childrenContainer = MapAccess::get($datum, $childrenContainerKey);
54
                } else {
55 12
                    if(!is_iterable($datum)) {
56 12
                        yield $level => $datum;
57
                    }
58 12
                    $childrenContainer = $datum;
59
                }
60 32
                if(is_iterable($childrenContainer)) {
61 24
                    foreach($childrenContainer as $child) {
62 24
                        $subLevelContainer[] = $child;
63
                    }
64
                }
65
            }
66 40
            $data = $subLevelContainer;
67 40
            ++$level;
68 40
        } while(count($subLevelContainer));
69
    }
70
71
    /**
72
     * Recursive helper method for wide traversal.
73
     *
74
     * @param iterable<DictAccess> $data
75
     * @param ?string $childrenContainerKey
76
     * @param int $initialLevel
77
     *
78
     * @return Generator
79
     */
80 40
    protected static function traverseDepthFirstRecursive(
81
        iterable $data,
82
        ?string $childrenContainerKey = null,
83
        int $initialLevel = 0
84
    ): Generator {
85 40
        $level = $initialLevel;
86 40
        foreach($data as $datum) {
87 32
            if($childrenContainerKey !== null) {
88 20
                yield $level => $datum;
89 20
                $childrenContainer = MapAccess::get($datum, $childrenContainerKey);
90
            } else {
91 12
                if(!is_iterable($datum)) {
92 12
                    yield $level => $datum;
93
                }
94 12
                $childrenContainer = $datum;
95
            }
96 32
            if(is_iterable($childrenContainer)) {
97 24
                yield from static::traverseDepthFirstRecursive($childrenContainer, $childrenContainerKey, $level + 1);
98
            }
99
        }
100
    }
101
}
102