1 | <?php |
||
2 | declare(strict_types = 1); |
||
3 | |||
4 | namespace Innmind\Immutable\Sequence; |
||
5 | |||
6 | use Innmind\Immutable\{ |
||
7 | Map, |
||
8 | Sequence, |
||
9 | Str, |
||
10 | Set, |
||
11 | Type, |
||
12 | ValidateArgument, |
||
13 | Exception\OutOfBoundException, |
||
14 | Exception\LogicException, |
||
15 | Exception\ElementNotFound, |
||
16 | Exception\CannotGroupEmptyStructure, |
||
17 | }; |
||
18 | |||
19 | /** |
||
20 | * @template T |
||
21 | */ |
||
22 | final class Primitive implements Implementation |
||
23 | { |
||
24 | private string $type; |
||
25 | private ValidateArgument $validate; |
||
26 | /** @var list<T> */ |
||
27 | private array $values; |
||
28 | private ?int $size; |
||
29 | |||
30 | /** |
||
31 | * @param T $values |
||
32 | */ |
||
33 | 259 | public function __construct(string $type, ...$values) |
|
34 | { |
||
35 | 259 | $this->type = $type; |
|
36 | 259 | $this->validate = Type::of($type); |
|
37 | 259 | $this->values = $values; |
|
0 ignored issues
–
show
|
|||
38 | 259 | $this->size = null; |
|
39 | 259 | } |
|
40 | |||
41 | 3 | public function type(): string |
|
42 | { |
||
43 | 3 | return $this->type; |
|
44 | } |
||
45 | |||
46 | 162 | public function size(): int |
|
47 | { |
||
48 | 162 | return $this->size ?? $this->size = \count($this->values); |
|
49 | } |
||
50 | |||
51 | 1 | public function count(): int |
|
52 | { |
||
53 | 1 | return $this->size(); |
|
54 | } |
||
55 | |||
56 | 37 | public function toArray(): array |
|
57 | { |
||
58 | 37 | return $this->values; |
|
59 | } |
||
60 | |||
61 | /** |
||
62 | * @throws OutOfBoundException |
||
63 | * |
||
64 | * @return T |
||
65 | */ |
||
66 | 34 | public function get(int $index) |
|
67 | { |
||
68 | 34 | if (!$this->has($index)) { |
|
69 | 4 | throw new OutOfBoundException; |
|
70 | } |
||
71 | |||
72 | 30 | return $this->values[$index]; |
|
73 | } |
||
74 | |||
75 | /** |
||
76 | * @param Implementation<T> $sequence |
||
77 | * |
||
78 | * @return self<T> |
||
79 | */ |
||
80 | 4 | public function diff(Implementation $sequence): self |
|
81 | { |
||
82 | return $this->filter(static function($value) use ($sequence): bool { |
||
83 | /** @var T $value */ |
||
84 | 4 | return !$sequence->contains($value); |
|
85 | 4 | }); |
|
86 | } |
||
87 | |||
88 | /** |
||
89 | * @return self<T> |
||
90 | */ |
||
91 | 97 | public function distinct(): self |
|
92 | { |
||
93 | 97 | return $this->reduce( |
|
94 | 97 | $this->clear(), |
|
95 | static function(self $values, $value): self { |
||
96 | 60 | if ($values->contains($value)) { |
|
97 | 8 | return $values; |
|
98 | } |
||
99 | |||
100 | 60 | return $values->add($value); |
|
101 | 97 | } |
|
102 | ); |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * @return self<T> |
||
107 | */ |
||
108 | 5 | public function drop(int $size): self |
|
109 | { |
||
110 | 5 | $self = $this->clear(); |
|
111 | 5 | $self->values = \array_slice($this->values, $size); |
|
0 ignored issues
–
show
It seems like
array_slice($this->values, $size) of type array is incompatible with the declared type Innmind\Immutable\Sequence\list of property $values .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
112 | |||
113 | 5 | return $self; |
|
114 | } |
||
115 | |||
116 | /** |
||
117 | * @return self<T> |
||
118 | */ |
||
119 | 2 | public function dropEnd(int $size): self |
|
120 | { |
||
121 | 2 | $self = $this->clear(); |
|
122 | 2 | $self->values = \array_slice($this->values, 0, $this->size() - $size); |
|
0 ignored issues
–
show
It seems like
array_slice($this->value... $this->size() - $size) of type array is incompatible with the declared type Innmind\Immutable\Sequence\list of property $values .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
123 | |||
124 | 2 | return $self; |
|
125 | } |
||
126 | |||
127 | /** |
||
128 | * @param Implementation<T> $sequence |
||
129 | */ |
||
130 | 7 | public function equals(Implementation $sequence): bool |
|
131 | { |
||
132 | 7 | return $this->values === $sequence->toArray(); |
|
133 | } |
||
134 | |||
135 | /** |
||
136 | * @param callable(T): bool $predicate |
||
137 | * |
||
138 | * @return self<T> |
||
139 | */ |
||
140 | 22 | public function filter(callable $predicate): self |
|
141 | { |
||
142 | 22 | $self = $this->clear(); |
|
143 | 22 | $self->values = \array_values(\array_filter( |
|
0 ignored issues
–
show
It seems like
array_values(array_filte...s->values, $predicate)) of type array is incompatible with the declared type Innmind\Immutable\Sequence\list of property $values .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
144 | 22 | $this->values, |
|
145 | 22 | $predicate |
|
146 | )); |
||
147 | |||
148 | 22 | return $self; |
|
149 | } |
||
150 | |||
151 | /** |
||
152 | * @param callable(T): void $function |
||
153 | */ |
||
154 | 4 | public function foreach(callable $function): void |
|
155 | { |
||
156 | 4 | foreach ($this->values as $value) { |
|
157 | 4 | $function($value); |
|
158 | } |
||
159 | 4 | } |
|
160 | |||
161 | /** |
||
162 | * @template D |
||
163 | * @param callable(T): D $discriminator |
||
164 | * |
||
165 | * @throws CannotGroupEmptyStructure |
||
166 | * |
||
167 | * @return Map<D, Sequence<T>> |
||
168 | */ |
||
169 | 6 | public function groupBy(callable $discriminator): Map |
|
170 | { |
||
171 | 6 | if ($this->empty()) { |
|
172 | 2 | throw new CannotGroupEmptyStructure; |
|
173 | } |
||
174 | |||
175 | 4 | $groups = null; |
|
176 | |||
177 | 4 | foreach ($this->values as $value) { |
|
178 | 4 | $key = $discriminator($value); |
|
179 | |||
180 | 4 | if ($groups === null) { |
|
181 | /** @var Map<D, Sequence<T>> */ |
||
182 | 4 | $groups = Map::of( |
|
183 | 4 | Type::determine($key), |
|
184 | 4 | Sequence::class, |
|
185 | ); |
||
186 | } |
||
187 | |||
188 | 4 | if ($groups->contains($key)) { |
|
189 | /** @var Sequence<T> */ |
||
190 | 4 | $group = $groups->get($key); |
|
191 | /** @var Sequence<T> */ |
||
192 | 4 | $group = ($group)($value); |
|
193 | |||
194 | 4 | $groups = ($groups)($key, $group); |
|
195 | } else { |
||
196 | 4 | $groups = ($groups)($key, Sequence::of($this->type, $value)); |
|
197 | } |
||
198 | } |
||
199 | |||
200 | /** @var Map<D, Sequence<T>> */ |
||
201 | 4 | return $groups; |
|
202 | } |
||
203 | |||
204 | /** |
||
205 | * @return T |
||
206 | */ |
||
207 | 5 | public function first() |
|
208 | { |
||
209 | 5 | return $this->get(0); |
|
210 | } |
||
211 | |||
212 | /** |
||
213 | * @return T |
||
214 | */ |
||
215 | 5 | public function last() |
|
216 | { |
||
217 | 5 | return $this->get($this->size() - 1); |
|
218 | } |
||
219 | |||
220 | /** |
||
221 | * @param T $element |
||
222 | */ |
||
223 | 110 | public function contains($element): bool |
|
224 | { |
||
225 | 110 | return \in_array($element, $this->values, true); |
|
226 | } |
||
227 | |||
228 | /** |
||
229 | * @param T $element |
||
230 | * |
||
231 | * @throws ElementNotFound |
||
232 | */ |
||
233 | 21 | public function indexOf($element): int |
|
234 | { |
||
235 | 21 | $index = \array_search($element, $this->values, true); |
|
236 | |||
237 | 21 | if ($index === false) { |
|
238 | 1 | throw new ElementNotFound($element); |
|
239 | } |
||
240 | |||
241 | 20 | return $index; |
|
242 | } |
||
243 | |||
244 | /** |
||
245 | * @psalm-suppress LessSpecificImplementedReturnType Don't why it complains |
||
246 | * |
||
247 | * @return self<int> |
||
248 | */ |
||
249 | 4 | public function indices(): self |
|
250 | { |
||
251 | 4 | if ($this->empty()) { |
|
252 | /** @var self<int> */ |
||
253 | 2 | return new self('int'); |
|
254 | } |
||
255 | |||
256 | /** @var self<int> */ |
||
257 | 2 | return new self('int', ...\range(0, $this->size() - 1)); |
|
258 | } |
||
259 | |||
260 | /** |
||
261 | * @param callable(T): T $function |
||
262 | * |
||
263 | * @return self<T> |
||
264 | */ |
||
265 | 5 | public function map(callable $function): self |
|
266 | { |
||
267 | /** |
||
268 | * @psalm-suppress MissingClosureParamType |
||
269 | * @psalm-suppress MissingClosureReturnType |
||
270 | */ |
||
271 | $function = function($value) use ($function) { |
||
272 | /** @var T $value */ |
||
273 | 5 | $returned = $function($value); |
|
274 | 5 | ($this->validate)($returned, 1); |
|
275 | |||
276 | 3 | return $returned; |
|
277 | 5 | }; |
|
278 | |||
279 | 5 | $self = clone $this; |
|
280 | 5 | $self->values = \array_map($function, $this->values); |
|
0 ignored issues
–
show
It seems like
array_map($function, $this->values) of type array is incompatible with the declared type Innmind\Immutable\Sequence\list of property $values .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
281 | |||
282 | 3 | return $self; |
|
283 | } |
||
284 | |||
285 | /** |
||
286 | * @param T $element |
||
287 | * |
||
288 | * @return self<T> |
||
289 | */ |
||
290 | 2 | public function pad(int $size, $element): self |
|
291 | { |
||
292 | 2 | $self = $this->clear(); |
|
293 | 2 | $self->values = \array_pad($this->values, $size, $element); |
|
0 ignored issues
–
show
It seems like
array_pad($this->values, $size, $element) of type array is incompatible with the declared type Innmind\Immutable\Sequence\list of property $values .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
294 | |||
295 | 2 | return $self; |
|
296 | } |
||
297 | |||
298 | /** |
||
299 | * @param callable(T): bool $predicate |
||
300 | * |
||
301 | * @return Map<bool, Sequence<T>> |
||
302 | */ |
||
303 | 4 | public function partition(callable $predicate): Map |
|
304 | { |
||
305 | /** @var list<T> */ |
||
306 | 4 | $truthy = []; |
|
307 | /** @var list<T> */ |
||
308 | 4 | $falsy = []; |
|
309 | |||
310 | 4 | foreach ($this->values as $value) { |
|
311 | 4 | if ($predicate($value) === true) { |
|
312 | 4 | $truthy[] = $value; |
|
313 | } else { |
||
314 | 4 | $falsy[] = $value; |
|
315 | } |
||
316 | } |
||
317 | |||
318 | /** @var Sequence<T> */ |
||
319 | 4 | $true = Sequence::of($this->type, ...$truthy); |
|
320 | /** @var Sequence<T> */ |
||
321 | 4 | $false = Sequence::of($this->type, ...$falsy); |
|
322 | |||
323 | /** |
||
324 | * @psalm-suppress InvalidScalarArgument |
||
325 | * @psalm-suppress InvalidArgument |
||
326 | */ |
||
327 | 4 | return Map::of('bool', Sequence::class) |
|
328 | 4 | (true, $true) |
|
329 | 4 | (false, $false); |
|
330 | } |
||
331 | |||
332 | /** |
||
333 | * @return self<T> |
||
334 | */ |
||
335 | 14 | public function slice(int $from, int $until): self |
|
336 | { |
||
337 | 14 | $self = $this->clear(); |
|
338 | 14 | $self->values = \array_slice( |
|
0 ignored issues
–
show
It seems like
array_slice($this->values, $from, $until - $from) of type array is incompatible with the declared type Innmind\Immutable\Sequence\list of property $values .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
339 | 14 | $this->values, |
|
340 | 14 | $from, |
|
341 | 14 | $until - $from, |
|
342 | ); |
||
343 | |||
344 | 14 | return $self; |
|
345 | } |
||
346 | |||
347 | /** |
||
348 | * @throws OutOfBoundException |
||
349 | * |
||
350 | * @return Sequence<Sequence<T>> |
||
351 | */ |
||
352 | 2 | public function splitAt(int $index): Sequence |
|
353 | { |
||
354 | /** @var Sequence<T> */ |
||
355 | 2 | $first = Sequence::of($this->type, ...$this->slice(0, $index)->toArray()); |
|
356 | /** @var Sequence<T> */ |
||
357 | 2 | $second = Sequence::of($this->type, ...$this->slice($index, $this->size())->toArray()); |
|
358 | |||
359 | /** @var Sequence<Sequence<T>> */ |
||
360 | 2 | return Sequence::of(Sequence::class, $first, $second); |
|
361 | } |
||
362 | |||
363 | /** |
||
364 | * @return self<T> |
||
365 | */ |
||
366 | 5 | public function take(int $size): self |
|
367 | { |
||
368 | 5 | return $this->slice(0, $size); |
|
369 | } |
||
370 | |||
371 | /** |
||
372 | * @return self<T> |
||
373 | */ |
||
374 | 2 | public function takeEnd(int $size): self |
|
375 | { |
||
376 | 2 | return $this->slice($this->size() - $size, $this->size()); |
|
377 | } |
||
378 | |||
379 | /** |
||
380 | * @param Implementation<T> $sequence |
||
381 | * |
||
382 | * @return self<T> |
||
383 | */ |
||
384 | 8 | public function append(Implementation $sequence): self |
|
385 | { |
||
386 | 8 | $self = $this->clear(); |
|
387 | /** @var list<T> */ |
||
388 | 8 | $self->values = \array_merge($this->values, $sequence->toArray()); |
|
389 | |||
390 | 8 | return $self; |
|
391 | } |
||
392 | |||
393 | /** |
||
394 | * @param Implementation<T> $sequence |
||
395 | * |
||
396 | * @return self<T> |
||
397 | */ |
||
398 | 14 | public function intersect(Implementation $sequence): self |
|
399 | { |
||
400 | return $this->filter(static function($value) use ($sequence): bool { |
||
401 | /** @var T $value */ |
||
402 | 14 | return $sequence->contains($value); |
|
403 | 14 | }); |
|
404 | } |
||
405 | |||
406 | /** |
||
407 | * @param T $element |
||
408 | * |
||
409 | * @return self<T> |
||
410 | */ |
||
411 | 155 | public function add($element): self |
|
412 | { |
||
413 | 155 | $self = clone $this; |
|
414 | 155 | $self->values[] = $element; |
|
415 | 155 | $self->size = $this->size() + 1; |
|
416 | |||
417 | 155 | return $self; |
|
418 | } |
||
419 | |||
420 | /** |
||
421 | * @param callable(T, T): int $function |
||
422 | * |
||
423 | * @return self<T> |
||
424 | */ |
||
425 | 4 | public function sort(callable $function): self |
|
426 | { |
||
427 | 4 | $self = clone $this; |
|
428 | 4 | \usort($self->values, $function); |
|
429 | |||
430 | 4 | return $self; |
|
431 | } |
||
432 | |||
433 | /** |
||
434 | * @template R |
||
435 | * @param R $carry |
||
436 | * @param callable(R, T): R $reducer |
||
437 | * |
||
438 | * @return R |
||
439 | */ |
||
440 | 224 | public function reduce($carry, callable $reducer) |
|
441 | { |
||
442 | /** @var R */ |
||
443 | 224 | return \array_reduce($this->values, $reducer, $carry); |
|
444 | } |
||
445 | |||
446 | /** |
||
447 | * @return self<T> |
||
448 | */ |
||
449 | 121 | public function clear(): Implementation |
|
450 | { |
||
451 | 121 | return new self($this->type); |
|
452 | } |
||
453 | |||
454 | /** |
||
455 | * @return self<T> |
||
456 | */ |
||
457 | 4 | public function reverse(): self |
|
458 | { |
||
459 | 4 | $self = clone $this; |
|
460 | 4 | $self->values = \array_reverse($this->values); |
|
0 ignored issues
–
show
It seems like
array_reverse($this->values) of type array is incompatible with the declared type Innmind\Immutable\Sequence\list of property $values .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||
461 | |||
462 | 4 | return $self; |
|
463 | } |
||
464 | |||
465 | 19 | public function empty(): bool |
|
466 | { |
||
467 | 19 | return !$this->has(0); |
|
468 | } |
||
469 | |||
470 | /** |
||
471 | * @template ST |
||
472 | * |
||
473 | * @param callable(T): \Generator<ST> $mapper |
||
474 | * |
||
475 | * @return Sequence<ST> |
||
476 | */ |
||
477 | 11 | public function toSequenceOf(string $type, callable $mapper): Sequence |
|
478 | { |
||
479 | /** @var Sequence<ST> */ |
||
480 | 11 | $sequence = Sequence::of($type); |
|
481 | |||
482 | 11 | foreach ($this->values as $value) { |
|
483 | 11 | foreach ($mapper($value) as $newValue) { |
|
484 | 11 | $sequence = ($sequence)($newValue); |
|
485 | } |
||
486 | } |
||
487 | |||
488 | 11 | return $sequence; |
|
489 | } |
||
490 | |||
491 | /** |
||
492 | * @template ST |
||
493 | * |
||
494 | * @param callable(T): \Generator<ST> $mapper |
||
495 | * |
||
496 | * @return Set<ST> |
||
497 | */ |
||
498 | 4 | public function toSetOf(string $type, callable $mapper): Set |
|
499 | { |
||
500 | /** @var Set<ST> */ |
||
501 | 4 | $set = Set::of($type); |
|
502 | |||
503 | 4 | foreach ($this->values as $value) { |
|
504 | 4 | foreach ($mapper($value) as $newValue) { |
|
505 | 4 | $set = ($set)($newValue); |
|
506 | } |
||
507 | } |
||
508 | |||
509 | 4 | return $set; |
|
510 | } |
||
511 | |||
512 | |||
513 | |||
514 | /** |
||
515 | * @template MT |
||
516 | * @template MS |
||
517 | * |
||
518 | * @param callable(T): \Generator<MT, MS> $mapper |
||
519 | * |
||
520 | * @return Map<MT, MS> |
||
521 | */ |
||
522 | 4 | public function toMapOf(string $key, string $value, callable $mapper): Map |
|
523 | { |
||
524 | /** @var Map<MT, MS> */ |
||
525 | 4 | $map = Map::of($key, $value); |
|
526 | |||
527 | 4 | foreach ($this->values as $value) { |
|
528 | 4 | foreach ($mapper($value) as $newKey => $newValue) { |
|
529 | 4 | $map = ($map)($newKey, $newValue); |
|
530 | } |
||
531 | } |
||
532 | |||
533 | 4 | return $map; |
|
534 | } |
||
535 | |||
536 | 53 | private function has(int $index): bool |
|
537 | { |
||
538 | 53 | return \array_key_exists($index, $this->values); |
|
539 | } |
||
540 | } |
||
541 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..