Completed
Push — master ( 64ed7a...0acc71 )
by Vitaly
02:24
created

findPrefixInExistingCommonPrefix()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 16
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 7
nc 3
nop 2
crap 3
1
<?php declare(strict_types=1);
2
/**
3
 * Created by Vitaly Iegorov <[email protected]>.
4
 * on 08.04.17 at 09:08
5
 */
6
namespace samsonframework\stringconditiontree\string;
7
8
use samsonframework\stringconditiontree\AbstractIterable;
9
10
/**
11
 * Class StructureCollection
12
 *
13
 * @author Vitaly Egorov <[email protected]>
14
 */
15
class StructureCollection extends AbstractIterable
16
{
17
    /** string Internal collection name */
18
    protected const COLLECTION_NAME = 'structures';
19
20
    /** @var Structure[] */
21
    protected $structures = [];
22
23
    /**
24
     * Create structures collection from array of strings.
25
     *
26
     * @param array $strings Strings array
27
     *
28
     * @return StructureCollection StructureCollection instance
29
     */
30 3
    public static function fromStringsArray(array $strings): StructureCollection
31
    {
32
        // Create internalCollection
33 3
        $structureCollection = new StructureCollection();
34 3
        foreach ($strings as $string) {
35 3
            $structureCollection->structures[$string] = new Structure($string);
36
        }
37
38 3
        return $structureCollection;
39
    }
40
41
    /**
42
     * Get collection of StructureCollection instances grouped by longest common prefixes.
43
     *
44
     * @return StructureCollection[] Longest common prefixes array of StructureCollection instances
45
     */
46 2
    public function getCommonPrefixesCollection(): array
47
    {
48 2
        $this->sort();
49
50
        /** @var StructureCollection[] $commonPrefixes */
51 2
        $commonPrefixes = [];
52
53
        /** @var Structure[] $usedStructures */
54 2
        $usedStructures = [];
55
56
        // Iterate sorted character group internalCollection
57 2
        foreach ($this->structures as $initialStructure) {
58 2
            $oneCommonPrefixFound = false;
59
            // Iterate all character group internalCollection again
60 2
            foreach ($this->structures as $comparedStructure) {
61
                // Ignore same internalCollection
62 2
                if ($initialStructure !== $comparedStructure) {
63 2
                    $foundPrefix = $initialStructure->getCommonPrefix($comparedStructure);
64
65
                    // If we have found common prefix between two structures
66 2
                    if ($foundPrefix !== '') {
67
                        /**
68
                         * Try to find if this prefix can be merged into already found common prefix
69
                         * as our structures collection is already sorted.
70
                         */
71 2
                        $foundPrefix = $this->findPrefixInExistingCommonPrefix($foundPrefix, $commonPrefixes);
72
73 2
                        $this->addToCommonPrefixesCollection(
74
                            $commonPrefixes,
75
                            $foundPrefix,
76
                            $comparedStructure,
77
                            $usedStructures
78
                        );
79
80 2
                        $oneCommonPrefixFound = true;
81
                    }
82
                }
83
            }
84
85 2
            if (!$oneCommonPrefixFound) {
86 1
                $this->addToCommonPrefixesCollection(
87
                    $commonPrefixes,
88 1
                    $initialStructure->getString(),
89
                    $initialStructure,
90
                    $usedStructures
91
                );
92
            }
93
        }
94
95 2
        return $this->sortStructureCollectionCollectionByPrefixes($commonPrefixes);
96
    }
97
98
    /**
99
     * Sort structures.
100
     *
101
     * @param bool $ascending Ascending sorting order
102
     */
103
    protected function sort(bool $ascending = true): void
104
    {
105
        // Sort internalCollection
106 2
        uasort($this->structures, function (Structure $initial, Structure $compared) {
107 2
            return $initial->compare($compared);
108 2
        });
109
110
        // Sort descending if needed
111 2
        $this->structures = $ascending ? array_reverse($this->structures) : $this->structures;
112 2
    }
113
114 2
    private function findPrefixInExistingCommonPrefix(string $prefix, array $existingPrefixes): string
115
    {
116
        /**
117
         * Try to find if this prefix can be merged into already found common prefix
118
         * as our structures collection is already sorted.
119
         */
120 2
        $foundPrefixStructure = new Structure($prefix);
121 2
        foreach ($existingPrefixes as $existingPrefix => $structures) {
122 2
            $internalPrefix = (new Structure($existingPrefix))->getCommonPrefix($foundPrefixStructure);
123 2
            if ($internalPrefix !== '') {
124 2
                return $internalPrefix;
125
            }
126
        }
127
128 2
        return $prefix;
129
    }
130
131 2
    private function addToCommonPrefixesCollection(
132
        array &$commonPrefixes,
133
        string $foundPrefix,
134
        Structure $comparedStructure,
135
        array &$usedStructures
136
    ): void {
137
        // Create new structure collection with common prefix
138 2
        if (!array_key_exists($foundPrefix, $commonPrefixes)) {
139 2
            $commonPrefixes[$foundPrefix] = new StructureCollection();
140
        }
141
142 2
        $newPrefix = substr($comparedStructure->getString(), strlen($foundPrefix));
143 2
        if ($newPrefix !== '' && !in_array($comparedStructure, $usedStructures, false)) {
144 2
            $usedStructures[] = $comparedStructure;
145
            // Add structure to structure collection
146 2
            $commonPrefixes[$foundPrefix]->add(new Structure($newPrefix));
147
        }
148 2
    }
149
150 2
    private function sortStructureCollectionCollectionByPrefixes(array $commonPrefixes): array
151
    {
152
        // Sort common prefixes
153 2
        $commonPrefixesCollection = new StructureCollection();
154 2
        foreach ($commonPrefixes as $prefix => $structures) {
155 2
            $commonPrefixesCollection->add(new Structure($prefix));
156
        }
157 2
        $commonPrefixesCollection->sort();
158
159 2
        $final = [];
160 2
        foreach ($commonPrefixesCollection as $prefix => $structureCollection) {
161 2
            $final[$prefix] = $commonPrefixes[$prefix];
162
        }
163
164 2
        return $final;
165
    }
166
167
    /**
168
     * Add structure to structure collection.
169
     *
170
     * @param Structure $structure Added structure
171
     */
172 2
    public function add(Structure $structure): void
173
    {
174 2
        $this->structures[$structure->getString()] = $structure;
175 2
    }
176
177
    /**
178
     * @param Structure $structure
179
     *
180
     * @return bool
181
     */
182
    public function has(Structure $structure): bool
183
    {
184
        return in_array($structure, $this->structures);
185
    }
186
}
187