MappedLinkedList::sort()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Smoren\Containers\Structs;
4
5
use Countable;
6
use Exception;
7
use IteratorAggregate;
8
use Smoren\Containers\Exceptions\LinkedListException;
9
use Smoren\Containers\Exceptions\MappedCollectionException;
10
use Smoren\Containers\Exceptions\MappedLinkedListException;
11
12
class MappedLinkedList implements IteratorAggregate, Countable
13
{
14
    /**
15
     * @var LinkedList
16
     */
17
    protected LinkedList $list;
18
    /**
19
     * @var MappedCollection
20
     */
21
    protected MappedCollection $positionsMap;
22
23
    /**
24
     * Create new list by merging several another lists
25
     * @param MappedLinkedList ...$lists
26
     * @return MappedLinkedList
27
     * @throws MappedLinkedListException|MappedCollectionException
28
     */
29
    public static function merge(MappedLinkedList ...$lists): MappedLinkedList
30
    {
31
        $result = new MappedLinkedList();
32
33
        foreach($lists as $list) {
34
            foreach($list as $id => $value) {
35
                $result->pushBack($id, $value);
36
            }
37
        }
38
39
        return $result;
40
    }
41
42
    /**
43
     * MappedLinkedList constructor.
44
     * @param array $inputMap
45
     * @param LinkedList|null $listObject
46
     * @param MappedCollection|null $positionMap
47
     * @throws MappedLinkedListException|MappedCollectionException
48
     */
49
    public function __construct(
50
        array $inputMap = [],
51
        ?LinkedList $listObject = null,
52
        ?MappedCollection $positionMap = null
53
    ) {
54
        $this->list = $listObject ?? new LinkedList();
55
        $this->positionsMap = $positionMap ?? new MappedCollection();
56
57
        foreach($inputMap as $id => $value) {
58
            $this->pushBack($id, $value);
59
        }
60
    }
61
62
    /**
63
     * Pushes element to front of list
64
     * @param string $id element ID
65
     * @param mixed $data data value of element
66
     * @return LinkedListItem
67
     * @throws MappedLinkedListException|MappedCollectionException
68
     */
69
    public function pushFront(string $id, $data): LinkedListItem
70
    {
71
        $this->checkNotExist($id);
72
73
        $position = $this->list->pushFront($data);
74
        $position->setExtra($id);
75
        $this->positionsMap->add($id, $position);
76
77
        return $position;
78
    }
79
80
    /**
81
     * Pushes element to back of list
82
     * @param string $id element ID
83
     * @param mixed $data data value of element
84
     * @return LinkedListItem
85
     * @throws MappedLinkedListException|MappedCollectionException
86
     */
87
    public function pushBack(string $id, $data): LinkedListItem
88
    {
89
        $this->checkNotExist($id);
90
91
        $position = $this->list->pushBack($data);
92
        $position->setExtra($id);
93
        $this->positionsMap->add($id, $position);
94
95
        return $position;
96
    }
97
98
    /**
99
     * Pushes new element to after target element position
100
     * @param string|null $idAfter element ID
101
     * @param string $id new element ID
102
     * @param mixed $data data value of new element
103
     * @return LinkedListItem
104
     * @throws MappedLinkedListException|MappedCollectionException
105
     */
106
    public function pushAfter(?string $idAfter, string $id, $data): LinkedListItem
107
    {
108
        if($idAfter !== null) {
109
            $this->checkExist($idAfter);
110
            $position = $this->positionsMap->get($idAfter);
111
        } else {
112
            $position = null;
113
        }
114
        $this->checkNotExist($id);
115
        $newPosition = $this->list->pushAfter($position, $data);
116
        $newPosition->setExtra($id);
117
        $this->positionsMap->add($id, $newPosition);
118
119
        return $newPosition;
120
    }
121
122
    /**
123
     * Pushes new element to before target element position
124
     * @param string|null $idBefore element ID
125
     * @param string $id new element ID
126
     * @param mixed $data data value of new element
127
     * @return LinkedListItem
128
     * @throws MappedLinkedListException|MappedCollectionException
129
     */
130
    public function pushBefore(?string $idBefore, string $id, $data): LinkedListItem
131
    {
132
        if($idBefore !== null) {
133
            $this->checkExist($idBefore);
134
            $position = $this->positionsMap->get($idBefore);
135
        } else {
136
            $position = null;
137
        }
138
        $this->checkNotExist($id);
139
        $newPosition = $this->list->pushBefore($position, $data);
140
        $newPosition->setExtra($id);
141
        $this->positionsMap->add($id, $newPosition);
142
143
        return $newPosition;
144
    }
145
146
    /**
147
     * Removes element from the front of list
148
     * @return array [id, value]
149
     * @throws MappedLinkedListException|LinkedListException|MappedCollectionException
150
     */
151
    public function popFront(): array
152
    {
153
        $this->checkNotEmpty();
154
155
        $position = $this->list->popFrontPosition();
156
        $id = $position->getExtra();
157
        $this->positionsMap->delete($id);
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type null; however, parameter $id of Smoren\Containers\Struct...pedCollection::delete() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
        $this->positionsMap->delete(/** @scrutinizer ignore-type */ $id);
Loading history...
158
159
        return [$id, $position->getData()];
160
    }
161
162
    /**
163
     * Removes element from the back of list
164
     * @return array [id, value]
165
     * @throws MappedLinkedListException|LinkedListException|MappedCollectionException
166
     */
167
    public function popBack(): array
168
    {
169
        $this->checkNotEmpty();
170
171
        $position = $this->list->popBackPosition();
172
        $id = $position->getExtra();
173
        $this->positionsMap->delete($id);
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type null; however, parameter $id of Smoren\Containers\Struct...pedCollection::delete() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

173
        $this->positionsMap->delete(/** @scrutinizer ignore-type */ $id);
Loading history...
174
175
        return [$id, $position->getData()];
176
    }
177
178
    /**
179
     * Removes element from target element ID
180
     * @param string $id element ID
181
     * @return LinkedListItem old position of element
182
     * @throws MappedLinkedListException|MappedCollectionException
183
     */
184
    public function delete(string $id): LinkedListItem
185
    {
186
        $this->checkExist($id);
187
188
        $position = $this->positionsMap->get($id);
189
        $this->positionsMap->delete($id);
190
191
        return $this->list->delete($position);
192
    }
193
194
    /**
195
     * Returns element with target element ID
196
     * @param string $id element ID
197
     * @return mixed element data value
198
     * @throws MappedLinkedListException|MappedCollectionException
199
     */
200
    public function get(string $id)
201
    {
202
        return $this->getPosition($id)->getData();
203
    }
204
205
    /**
206
     * Returns element position with target element ID
207
     * @param string $id element ID
208
     * @return LinkedListItem position of element
209
     * @throws MappedLinkedListException|MappedCollectionException
210
     */
211
    public function getPosition(string $id): LinkedListItem
212
    {
213
        $this->checkExist($id);
214
        return $this->positionsMap->get($id);
215
    }
216
217
    /**
218
     * Swaps two elements
219
     * @param string $lhsId first element ID
220
     * @param string $rhsId second element ID
221
     * @return $this
222
     * @throws MappedCollectionException|MappedLinkedListException
223
     */
224
    public function swap(string $lhsId, string $rhsId): self
225
    {
226
        $this->checkExist($lhsId);
227
        $this->checkExist($rhsId);
228
229
        $this->list->swap($this->positionsMap->get($lhsId), $this->positionsMap->get($rhsId));
230
231
        return $this;
232
    }
233
234
    /**
235
     * Clears collection
236
     * @return $this
237
     */
238
    public function clear(): self
239
    {
240
        $this->list->clear();
241
        $this->positionsMap->clear();
242
        return $this;
243
    }
244
245
    /**
246
     * Sorts element via comparator callback
247
     * @param callable $comparator comparator callback
248
     * @return $this
249
     * @throws Exception
250
     */
251
    public function sort(callable $comparator): self
252
    {
253
        $this->list->sort($comparator);
254
        return $this;
255
    }
256
257
    /**
258
     * Converts list to array
259
     * @return array
260
     */
261
    public function toArray(): array
262
    {
263
        $result = [];
264
265
        /**
266
         * @var LinkedListItem $position
267
         * @var mixed $value
268
         */
269
        foreach($this->list as $position => $value) {
270
            $result[$position->getExtra()] = $value;
271
        }
272
273
        return $result;
274
    }
275
276
    /**
277
     * Returns true if element with such ID exists in collection
278
     * @param string $id element ID
279
     * @return bool
280
     */
281
    public function exist(string $id): bool
282
    {
283
        return $this->positionsMap->exist($id);
284
    }
285
286
    /**
287
     * Checks if element with such ID exists
288
     * @param string $id element ID
289
     * @return $this
290
     * @throws MappedLinkedListException
291
     */
292
    public function checkExist(string $id): self
293
    {
294
        if(!$this->exist($id)) {
295
            throw new MappedLinkedListException(
296
                "ID '{$id}' not exists",
297
                MappedLinkedListException::STATUS_ID_NOT_EXIST
298
            );
299
        }
300
        return $this;
301
    }
302
303
    /**
304
     * Checks if element with such ID not exists
305
     * @param string $id element ID
306
     * @return $this
307
     * @throws MappedLinkedListException
308
     */
309
    public function checkNotExist(string $id): self
310
    {
311
        if($this->exist($id)) {
312
            throw new MappedLinkedListException(
313
                "ID '{$id}' exists",
314
                MappedLinkedListException::STATUS_ID_EXIST
315
            );
316
        }
317
        return $this;
318
    }
319
320
    /**
321
     * Checks if collection is not empty
322
     * @return $this
323
     * @throws MappedLinkedListException
324
     */
325
    public function checkNotEmpty(): self
326
    {
327
        if(!$this->count()) {
328
            throw new MappedLinkedListException(
329
                "collection is empty",
330
                MappedLinkedListException::STATUS_EMPTY
331
            );
332
        }
333
        return $this;
334
    }
335
336
    /**
337
     * Returns LinkedList object of collection
338
     * @return LinkedList
339
     */
340
    public function getList(): LinkedList
341
    {
342
        return $this->list;
343
    }
344
345
    /**
346
     * Returns LinkedList object of collection
347
     * @return MappedCollection
348
     */
349
    public function getPositionsMap(): MappedCollection
350
    {
351
        return $this->positionsMap;
352
    }
353
354
    /**
355
     * @inheritDoc
356
     * @return MappedLinkedListIterator
357
     */
358
    public function getIterator(): MappedLinkedListIterator
359
    {
360
        return new MappedLinkedListIterator($this);
361
    }
362
363
    /**
364
     * @inheritDoc
365
     */
366
    public function count(): int
367
    {
368
        return $this->list->count();
369
    }
370
371
    /**
372
     * Clones object
373
     */
374
    public function __clone()
375
    {
376
        $this->list = clone $this->list;
377
        $this->positionsMap = clone $this->positionsMap;
378
        $this->positionsMap->replaceAll($this->list->getPositionsArray(function(LinkedListItem $item) {
379
            return $item->getExtra();
380
        }));
381
    }
382
}
383