• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

elephox-dev / framework / 4877852653

pending completion
4877852653

push

github

Ricardo Boss
WIP

38 of 38 new or added lines in 6 files covered. (100.0%)

3863 of 5835 relevant lines covered (66.2%)

8.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.46
/modules/Collection/src/IsKeyedEnumerable.php
1
<?php
2
declare(strict_types=1);
3

4
namespace Elephox\Collection;
5

6
use AppendIterator;
7
use CachingIterator;
8
use CallbackFilterIterator;
9
use Countable;
10
use Elephox\Collection\Contract\GenericEnumerable;
11
use Elephox\Collection\Contract\GenericGroupedKeyedEnumerable;
12
use Elephox\Collection\Contract\GenericKeyedEnumerable;
13
use Elephox\Collection\Contract\GenericKeyValuePair;
14
use Elephox\Collection\Contract\GenericOrderedEnumerable;
15
use Elephox\Collection\Iterator\FlipIterator;
16
use Elephox\Collection\Iterator\GroupingIterator;
17
use Elephox\Collection\Iterator\KeySelectIterator;
18
use Elephox\Collection\Iterator\OrderedIterator;
19
use Elephox\Collection\Iterator\ReverseIterator;
20
use Elephox\Collection\Iterator\SelectIterator;
21
use Elephox\Collection\Iterator\UniqueByIterator;
22
use Elephox\Collection\Iterator\WhileIterator;
23
use EmptyIterator;
24
use InvalidArgumentException;
25
use Iterator;
26
use IteratorIterator;
27
use JetBrains\PhpStorm\ExpectedValues;
28
use JsonException;
29
use LimitIterator;
30
use MultipleIterator as ParallelIterator;
31
use NoRewindIterator;
32
use OutOfBoundsException;
33
use RecursiveArrayIterator;
34
use RecursiveIteratorIterator;
35
use Stringable;
36
use Traversable;
37

38
/**
39
 * @psalm-type NonNegativeInteger = int<0,max>
40
 *
41
 * @template TIteratorKey
42
 * @template TSource
43
 */
44
trait IsKeyedEnumerable
45
{
46
        // TODO: rewrite more functions to use iterators
47

48
        /**
49
         * @return Traversable<TIteratorKey, TSource>
50
         */
51
        abstract public function getIterator(): Traversable;
52

53
        /**
54
         * @template TAccumulate
55
         *
56
         * @param callable(TAccumulate, TSource, TIteratorKey): TAccumulate $accumulator
57
         * @param TAccumulate $seed
58
         *
59
         * @return TAccumulate
60
         */
61
        public function aggregate(callable $accumulator, mixed $seed = null): mixed
62
        {
63
                $result = $seed;
2✔
64

65
                /**
66
                 * @var TIteratorKey $elementKey
67
                 */
68
                foreach ($this->getIterator() as $elementKey => $element) {
2✔
69
                        $result = $accumulator($result, $element, $elementKey);
2✔
70
                }
71

72
                return $result;
2✔
73
        }
74

75
        public function all(callable $predicate): bool
76
        {
77
                foreach ($this->getIterator() as $elementKey => $element) {
1✔
78
                        if (!$predicate($element, $elementKey)) {
1✔
79
                                return false;
1✔
80
                        }
81
                }
82

83
                return true;
1✔
84
        }
85

86
        public function any(?callable $predicate = null): bool
87
        {
88
                foreach ($this->getIterator() as $elementKey => $element) {
1✔
89
                        if ($predicate === null || $predicate($element, $elementKey)) {
1✔
90
                                return true;
1✔
91
                        }
92
                }
93

94
                return false;
1✔
95
        }
96

97
        public function append(mixed $key, mixed $value): GenericKeyedEnumerable
98
        {
99
                return new KeyedEnumerable(function () use ($value, $key) {
1✔
100
                        yield from $this->getIterator();
1✔
101

102
                        yield $key => $value;
1✔
103
                });
1✔
104
        }
105

106
        public function appendAll(iterable $values): GenericKeyedEnumerable
107
        {
108
                return new KeyedEnumerable(function () use ($values) {
1✔
109
                        yield from $this->getIterator();
1✔
110
                        yield from $values;
1✔
111
                });
1✔
112
        }
113

114
        /**
115
         * @param callable(TSource, TIteratorKey): numeric $selector
116
         *
117
         * @return numeric
118
         */
119
        public function average(callable $selector): int|float|string
120
        {
121
                $sum = null;
2✔
122
                $count = 0;
2✔
123

124
                /**
125
                 * @var TIteratorKey $elementKey
126
                 */
127
                foreach ($this->getIterator() as $elementKey => $element) {
2✔
128
                        $value = $selector($element, $elementKey);
1✔
129

130
                        /** @var null|numeric $sum */
131
                        if ($sum === null) {
1✔
132
                                $sum = $value;
1✔
133
                        } else {
134
                                $sum += $value;
1✔
135
                        }
136

137
                        $count++;
1✔
138
                }
139

140
                if ($count === 0) {
2✔
141
                        throw new EmptySequenceException();
1✔
142
                }
143

144
                /** @var numeric $sum */
145
                return $sum / $count;
1✔
146
        }
147

148
        /**
149
         * @param NonNegativeInteger $size
150
         *
151
         * @return GenericEnumerable<non-empty-list<TSource>>
152
         */
153
        public function chunk(int $size): GenericEnumerable
154
        {
155
                if ($size <= 0) {
2✔
156
                        throw new InvalidArgumentException('Chunk size must be greater than zero.');
1✔
157
                }
158

159
                /** @var GenericEnumerable<non-empty-list<TSource>> */
160
                return new Enumerable(function () use ($size) {
1✔
161
                        $chunk = [];
1✔
162
                        /** @var TSource $element */
163
                        foreach ($this->getIterator() as $element) {
1✔
164
                                if (count($chunk) === $size) {
1✔
165
                                        yield $chunk;
1✔
166

167
                                        $chunk = [$element];
1✔
168
                                } else {
169
                                        $chunk[] = $element;
1✔
170
                                }
171
                        }
172

173
                        if ($chunk) {
1✔
174
                                yield $chunk;
1✔
175
                        }
176
                });
1✔
177
        }
178

179
        public function concat(GenericKeyedEnumerable ...$other): GenericKeyedEnumerable
180
        {
181
                return new KeyedEnumerable(function () use ($other) {
28✔
182
                        yield from $this;
28✔
183

184
                        foreach ($other as $enumerable) {
28✔
185
                                yield from $enumerable;
28✔
186
                        }
187
                });
28✔
188
        }
189

190
        public function contains(mixed $value, ?callable $comparer = null): bool
191
        {
192
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
193

194
                foreach ($this->getIterator() as $element) {
1✔
195
                        if ($comparer($value, $element)) {
1✔
196
                                return true;
1✔
197
                        }
198
                }
199

200
                return false;
1✔
201
        }
202

203
        public function containsKey(mixed $key, ?callable $comparer = null): bool
204
        {
205
                $comparer ??= DefaultEqualityComparer::same(...);
×
206

207
                foreach ($this->getIterator() as $elementKey => $value) {
×
208
                        if ($comparer($key, $elementKey)) {
×
209
                                return true;
×
210
                        }
211
                }
212

213
                return false;
×
214
        }
215

216
        /**
217
         * @param null|callable(TSource, TIteratorKey, Iterator<TIteratorKey, TSource>): bool $predicate
218
         *
219
         * @return NonNegativeInteger
220
         */
221
        public function count(?callable $predicate = null): int
222
        {
223
                $iterator = $this->getIterator();
16✔
224
                if ($predicate !== null) {
16✔
225
                        if (!($iterator instanceof Iterator)) {
1✔
226
                                $iterator = new IteratorIterator($iterator);
×
227
                        }
228
                        $iterator = new CallbackFilterIterator($iterator, $predicate);
1✔
229
                } elseif ($iterator instanceof Countable) {
16✔
230
                        /** @var NonNegativeInteger */
231
                        return $iterator->count();
10✔
232
                }
233

234
                return iterator_count($iterator);
9✔
235
        }
236

237
        /**
238
         * @param null|callable(TSource, TSource): bool $comparer
239
         *
240
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
241
         */
242
        public function distinct(?callable $comparer = null): GenericKeyedEnumerable
243
        {
244
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
245
                $identity = static fn (mixed $element): mixed => $element;
1✔
246

247
                /**
248
                 * @var Closure(TSource, TSource): bool $comparer
249
                 * @var Closure(TSource): TSource $identity
250
                 */
251
                return $this->distinctBy($identity, $comparer);
1✔
252
        }
253

254
        /**
255
         * @template TCompareKey
256
         *
257
         * @param callable(TSource): TCompareKey $keySelector
258
         * @param null|callable(TCompareKey, TCompareKey): bool $comparer
259
         *
260
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
261
         */
262
        public function distinctBy(callable $keySelector, ?callable $comparer = null): GenericKeyedEnumerable
263
        {
264
                $comparer ??= DefaultEqualityComparer::same(...);
2✔
265

266
                $iterator = $this->getIterator();
2✔
267
                if (!($iterator instanceof Iterator)) {
2✔
268
                        $iterator = new IteratorIterator($iterator);
×
269
                }
270

271
                /**
272
                 * @var Closure(TSource, TSource): bool $comparer
273
                 * @var Closure(TSource): TSource $keySelector
274
                 */
275
                return new KeyedEnumerable(new UniqueByIterator($iterator, $keySelector(...), $comparer(...)));
2✔
276
        }
277

278
        /**
279
         * @param GenericKeyedEnumerable<TSource, TIteratorKey> $other
280
         * @param null|callable(TSource, TSource): bool $comparer
281
         *
282
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
283
         */
284
        public function except(GenericKeyedEnumerable $other, ?callable $comparer = null): GenericKeyedEnumerable
285
        {
286
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
287

288
                return $this->exceptBy($other, static fn (mixed $element): mixed => $element, $comparer);
1✔
289
        }
290

291
        /**
292
         * @template TCompareKey
293
         *
294
         * @param GenericKeyedEnumerable<TIteratorKey, TSource> $other
295
         * @param callable(TSource, TIteratorKey): TCompareKey $keySelector
296
         * @param null|callable(TCompareKey, TCompareKey): bool $comparer
297
         *
298
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
299
         */
300
        public function exceptBy(GenericKeyedEnumerable $other, callable $keySelector, ?callable $comparer = null): GenericKeyedEnumerable
301
        {
302
                $comparer ??= DefaultEqualityComparer::same(...);
2✔
303

304
                return new KeyedEnumerable(function () use ($other, $keySelector, $comparer) {
2✔
305
                        /** @var Iterator<TCompareKey, TSource> $otherKeys */
306
                        $otherKeys = new CachingIterator(new SelectIterator($other->getIterator(), $keySelector(...)), CachingIterator::FULL_CACHE);
2✔
307

308
                        foreach ($this->getIterator() as $elementKey => $element) {
2✔
309
                                $key = $keySelector($element, $elementKey);
2✔
310

311
                                foreach ($otherKeys as $otherKey) {
2✔
312
                                        if ($comparer($key, $otherKey)) {
2✔
313
                                                continue 2;
2✔
314
                                        }
315
                                }
316

317
                                yield $elementKey => $element;
2✔
318
                        }
319
                });
2✔
320
        }
321

322
        public function first(?callable $predicate = null): mixed
323
        {
324
                foreach ($this->getIterator() as $elementKey => $element) {
1✔
325
                        if ($predicate === null || $predicate($element, $elementKey)) {
1✔
326
                                return $element;
1✔
327
                        }
328
                }
329

330
                throw new EmptySequenceException();
1✔
331
        }
332

333
        public function firstKey(?callable $predicate = null): mixed
334
        {
335
                foreach ($this->getIterator() as $elementKey => $element) {
1✔
336
                        if ($predicate === null || $predicate($element, $elementKey)) {
1✔
337
                                return $elementKey;
1✔
338
                        }
339
                }
340

341
                throw new EmptySequenceException();
1✔
342
        }
343

344
        public function firstPair(?callable $predicate = null): GenericKeyValuePair
345
        {
346
                foreach ($this->getIterator() as $elementKey => $element) {
1✔
347
                        if ($predicate === null || $predicate($element, $elementKey)) {
1✔
348
                                return new KeyValuePair($elementKey, $element);
1✔
349
                        }
350
                }
351

352
                throw new EmptySequenceException();
1✔
353
        }
354

355
        /**
356
         * @template TDefault
357
         *
358
         * @param TDefault $defaultValue
359
         * @param null|callable(TSource, TIteratorKey): bool $predicate
360
         *
361
         * @return TDefault|TSource
362
         */
363
        public function firstOrDefault(mixed $defaultValue, ?callable $predicate = null): mixed
364
        {
365
                foreach ($this->getIterator() as $elementKey => $element) {
2✔
366
                        if ($predicate === null || $predicate($element, $elementKey)) {
2✔
367
                                return $element;
2✔
368
                        }
369
                }
370

371
                return $defaultValue;
2✔
372
        }
373

374
        /**
375
         * @template TDefault
376
         *
377
         * @param TDefault $defaultKey
378
         * @param null|callable(TSource, TIteratorKey): bool $predicate
379
         *
380
         * @return TDefault|TIteratorKey
381
         */
382
        public function firstKeyOrDefault(mixed $defaultKey, ?callable $predicate = null): mixed
383
        {
384
                foreach ($this->getIterator() as $elementKey => $element) {
18✔
385
                        if ($predicate === null || $predicate($element, $elementKey)) {
13✔
386
                                return $elementKey;
11✔
387
                        }
388
                }
389

390
                return $defaultKey;
9✔
391
        }
392

393
        /**
394
         * @param null|GenericKeyValuePair<TIteratorKey, TSource> $defaultPair
395
         * @param null|callable(TSource, TIteratorKey): bool $predicate
396
         *
397
         * @return null|GenericKeyValuePair<TIteratorKey, TSource>
398
         */
399
        public function firstPairOrDefault(?GenericKeyValuePair $defaultPair, ?callable $predicate = null): ?GenericKeyValuePair
400
        {
401
                foreach ($this->getIterator() as $elementKey => $element) {
1✔
402
                        if ($predicate === null || $predicate($element, $elementKey)) {
1✔
403
                                return new KeyValuePair($elementKey, $element);
1✔
404
                        }
405
                }
406

407
                return $defaultPair;
1✔
408
        }
409

410
        /**
411
         * @return GenericKeyedEnumerable<TSource, TIteratorKey>
412
         */
413
        public function flip(): GenericKeyedEnumerable
414
        {
415
                $iterator = $this->getIterator();
1✔
416
                if (!($iterator instanceof Iterator)) {
1✔
417
                        $iterator = new IteratorIterator($iterator);
×
418
                }
419

420
                return new KeyedEnumerable(new FlipIterator($iterator));
1✔
421
        }
422

423
        /**
424
         * @return GenericEnumerable<TSource>
425
         */
426
        public function flatten(): GenericEnumerable
427
        {
428
                return new Enumerable(function () {
1✔
429
                        $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($this->getIterator()));
1✔
430
                        foreach ($it as $v) {
1✔
431
                                yield $v;
1✔
432
                        }
433
                });
1✔
434
        }
435

436
        /**
437
         * @param callable(TSource, TIteratorKey): void $callback
438
         */
439
        public function forEach(callable $callback): void
440
        {
441
                foreach ($this->getIterator() as $key => $element) {
1✔
442
                        $callback($element, $key);
1✔
443
                }
444
        }
445

446
        /**
447
         * @template TGroupKey
448
         *
449
         * @param callable(TSource, TIteratorKey): TGroupKey $keySelector
450
         * @param null|callable(TSource, TSource): bool $comparer
451
         *
452
         * @return GenericGroupedKeyedEnumerable<TGroupKey, TIteratorKey, TSource>
453
         */
454
        public function groupBy(callable $keySelector, ?callable $comparer = null): GenericGroupedKeyedEnumerable
455
        {
456
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
457

458
                return new GroupedKeyedEnumerable(new GroupingIterator($this->getIterator(), $keySelector(...), $comparer(...)));
1✔
459
        }
460

461
        public function implode(string $separator = ', ', ?callable $toString = null): string
462
        {
463
                $toString ??= static fn (mixed $v, mixed $k): string => (string) $v;
5✔
464
                $strings = [];
5✔
465

466
                foreach ($this->getIterator() as $key => $value) {
5✔
467
                        $strings[] = $toString($value, $key);
5✔
468
                }
469

470
                return implode($separator, $strings);
5✔
471
        }
472

473
        /**
474
         * @param GenericKeyedEnumerable<TIteratorKey, TSource> $other
475
         * @param null|callable(TSource, TSource): bool $comparer
476
         *
477
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
478
         */
479
        public function intersect(GenericKeyedEnumerable $other, ?callable $comparer = null): GenericKeyedEnumerable
480
        {
481
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
482

483
                return $this->intersectBy($other, static fn ($element): mixed => $element, $comparer);
1✔
484
        }
485

486
        /**
487
         * @template TKey
488
         *
489
         * @param GenericKeyedEnumerable<TIteratorKey, TSource> $other
490
         * @param callable(TSource, TIteratorKey): TKey $keySelector
491
         * @param null|callable(TSource, TSource): bool $comparer
492
         *
493
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
494
         */
495
        public function intersectBy(GenericKeyedEnumerable $other, callable $keySelector, ?callable $comparer = null): GenericKeyedEnumerable
496
        {
497
                $comparer ??= DefaultEqualityComparer::same(...);
2✔
498

499
                return new KeyedEnumerable(function () use ($other, $keySelector, $comparer) {
2✔
500
                        $otherKeys = [];
2✔
501
                        foreach ($other->getIterator() as $otherElementKey => $otherElement) {
2✔
502
                                $otherKeys[] = $keySelector($otherElement, $otherElementKey);
2✔
503
                        }
504

505
                        foreach ($this->getIterator() as $elementKey => $element) {
2✔
506
                                $key = $keySelector($element, $elementKey);
2✔
507

508
                                foreach ($otherKeys as $otherKey) {
2✔
509
                                        if ($comparer($key, $otherKey)) {
2✔
510
                                                yield $elementKey => $element;
2✔
511

512
                                                continue 2;
2✔
513
                                        }
514
                                }
515
                        }
516
                });
2✔
517
        }
518

519
        public function isEmpty(): bool
520
        {
521
                return $this->count() === 0;
16✔
522
        }
523

524
        public function isNotEmpty(): bool
525
        {
526
                return !$this->isEmpty();
1✔
527
        }
528

529
        /**
530
         * @template TInner
531
         * @template TInnerIteratorKey
532
         * @template TCompareKey
533
         * @template TResult
534
         *
535
         * @param GenericKeyedEnumerable<TInnerIteratorKey, TInner> $inner
536
         * @param callable(TSource, TIteratorKey): TCompareKey $outerKeySelector
537
         * @param callable(TInner, TInnerIteratorKey): TCompareKey $innerKeySelector
538
         * @param callable(TSource, TInner, TIteratorKey, TInnerIteratorKey): TResult $resultSelector
539
         * @param null|callable(TCompareKey, TCompareKey): bool $comparer
540
         *
541
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
542
         */
543
        public function join(GenericKeyedEnumerable $inner, callable $outerKeySelector, callable $innerKeySelector, callable $resultSelector, ?callable $comparer = null): GenericKeyedEnumerable
544
        {
545
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
546

547
                return new KeyedEnumerable(function () use ($inner, $outerKeySelector, $innerKeySelector, $resultSelector, $comparer) {
1✔
548
                        $innerKeys = [];
1✔
549
                        $innerElements = [];
1✔
550
                        $innerElementKeys = [];
1✔
551
                        foreach ($inner->getIterator() as $innerElementKey => $innerElement) {
1✔
552
                                $innerKeys[] = $innerKeySelector($innerElement, $innerElementKey);
1✔
553
                                $innerElements[] = $innerElement;
1✔
554
                                $innerElementKeys[] = $innerElementKey;
1✔
555
                        }
556

557
                        foreach ($this->getIterator() as $outerElementKey => $outerElement) {
1✔
558
                                $outerKey = $outerKeySelector($outerElement, $outerElementKey);
1✔
559

560
                                foreach ($innerKeys as $index => $innerKey) {
1✔
561
                                        if ($comparer($outerKey, $innerKey)) {
1✔
562
                                                yield $outerElementKey => $resultSelector($outerElement, $innerElements[$index], $outerElementKey, $innerElementKeys[$index]);
1✔
563
                                        }
564
                                }
565
                        }
566
                });
1✔
567
        }
568

569
        public function last(?callable $predicate = null): mixed
570
        {
571
                $last = null;
2✔
572
                foreach ($this->getIterator() as $elementKey => $element) {
2✔
573
                        if ($predicate === null || $predicate($element, $elementKey)) {
1✔
574
                                $last = $element;
1✔
575
                        }
576
                }
577

578
                if ($last === null) {
2✔
579
                        throw new EmptySequenceException();
1✔
580
                }
581

582
                return $last;
1✔
583
        }
584

585
        public function lastOrDefault(mixed $default, ?callable $predicate = null): mixed
586
        {
587
                $last = null;
1✔
588
                foreach ($this->getIterator() as $elementKey => $element) {
1✔
589
                        if ($predicate === null || $predicate($element, $elementKey)) {
1✔
590
                                $last = $element;
1✔
591
                        }
592
                }
593

594
                return $last ?? $default;
1✔
595
        }
596

597
        /**
598
         * @param callable(TSource, TIteratorKey): numeric $selector
599
         *
600
         * @return numeric
601
         */
602
        public function max(callable $selector): int|float|string
603
        {
604
                /** @var Iterator<TIteratorKey, TSource> $iterator */
605
                $iterator = $this->getIterator();
2✔
606
                if (!($iterator instanceof Iterator)) {
2✔
607
                        $iterator = new IteratorIterator($iterator);
×
608
                }
609
                $iterator->rewind();
2✔
610
                if (!$iterator->valid()) {
2✔
611
                        throw new EmptySequenceException();
1✔
612
                }
613

614
                $max = $selector($iterator->current(), $iterator->key());
1✔
615
                $iterator->next();
1✔
616

617
                while ($iterator->valid()) {
1✔
618
                        $max = max($max, $selector($iterator->current(), $iterator->key()));
1✔
619

620
                        $iterator->next();
1✔
621
                }
622

623
                return $max;
1✔
624
        }
625

626
        /**
627
         * @param callable(TSource, TIteratorKey): numeric $selector
628
         *
629
         * @return numeric
630
         */
631
        public function min(callable $selector): int|float|string
632
        {
633
                /** @var Iterator<TIteratorKey, TSource> $iterator */
634
                $iterator = $this->getIterator();
2✔
635
                if (!($iterator instanceof Iterator)) {
2✔
636
                        $iterator = new IteratorIterator($iterator);
×
637
                }
638
                $iterator->rewind();
2✔
639
                if (!$iterator->valid()) {
2✔
640
                        throw new EmptySequenceException();
1✔
641
                }
642

643
                $min = $selector($iterator->current(), $iterator->key());
1✔
644
                $iterator->next();
1✔
645

646
                while ($iterator->valid()) {
1✔
647
                        $min = min($min, $selector($iterator->current(), $iterator->key()));
1✔
648

649
                        $iterator->next();
1✔
650
                }
651

652
                return $min;
1✔
653
        }
654

655
        /**
656
         * @template TCompareKey
657
         *
658
         * @param callable(TSource, TIteratorKey): TCompareKey $keySelector
659
         * @param null|callable(TCompareKey, TCompareKey): int $comparer
660
         *
661
         * @return GenericOrderedEnumerable<TSource>
662
         */
663
        public function orderBy(callable $keySelector, ?callable $comparer = null): GenericOrderedEnumerable
664
        {
665
                $comparer ??= DefaultEqualityComparer::compare(...);
2✔
666

667
                return new OrderedEnumerable(new OrderedIterator($this->getIterator(), $keySelector(...), $comparer(...)));
2✔
668
        }
669

670
        /**
671
         * @template TCompareKey
672
         *
673
         * @param callable(TSource, TIteratorKey): TCompareKey $keySelector
674
         * @param null|callable(TCompareKey, TCompareKey): int $comparer
675
         *
676
         * @return GenericOrderedEnumerable<TSource>
677
         */
678
        public function orderByDescending(callable $keySelector, ?callable $comparer = null): GenericOrderedEnumerable
679
        {
680
                $comparer ??= DefaultEqualityComparer::compare(...);
28✔
681
                $comparer = DefaultEqualityComparer::invert($comparer);
28✔
682
                /** @var Closure(mixed, mixed): int $comparer */
683

684
                return new OrderedEnumerable(new OrderedIterator($this->getIterator(), $keySelector(...), $comparer(...)));
28✔
685
        }
686

687
        public function prepend(mixed $key, mixed $value): GenericKeyedEnumerable
688
        {
689
                return new KeyedEnumerable(function () use ($value, $key) {
1✔
690
                        yield $key => $value;
1✔
691

692
                        yield from $this->getIterator();
1✔
693
                });
1✔
694
        }
695

696
        public function prependAll(iterable $values): GenericKeyedEnumerable
697
        {
698
                return new KeyedEnumerable(function () use ($values) {
1✔
699
                        yield from $values;
1✔
700

701
                        yield from $this->getIterator();
1✔
702
                });
1✔
703
        }
704

705
        public function reverse(bool $preserveKeys = true): GenericKeyedEnumerable
706
        {
707
                return new KeyedEnumerable(new ReverseIterator($this->getIterator(), $preserveKeys));
3✔
708
        }
709

710
        /**
711
         * @template TResult
712
         *
713
         * @param callable(TSource, TIteratorKey): TResult $selector
714
         *
715
         * @return GenericKeyedEnumerable<TIteratorKey, TResult>
716
         */
717
        public function select(callable $selector): GenericKeyedEnumerable
718
        {
719
                $iterator = $this->getIterator();
70✔
720
                if (!($iterator instanceof Iterator)) {
70✔
721
                        $iterator = new IteratorIterator($iterator);
×
722
                }
723

724
                return new KeyedEnumerable(new SelectIterator($iterator, $selector(...)));
70✔
725
        }
726

727
        /**
728
         * @template TResult
729
         *
730
         * @param callable(TIteratorKey, TSource): TResult $keySelector
731
         *
732
         * @return GenericKeyedEnumerable<TResult, TSource>
733
         */
734
        public function selectKeys(callable $keySelector): GenericKeyedEnumerable
735
        {
736
                $iterator = $this->getIterator();
28✔
737
                if (!($iterator instanceof Iterator)) {
28✔
738
                        $iterator = new IteratorIterator($iterator);
×
739
                }
740

741
                return new KeyedEnumerable(new KeySelectIterator($iterator, $keySelector(...)));
28✔
742
        }
743

744
        /**
745
         * @template TCollection
746
         * @template TCollectionKey
747
         * @template TResult
748
         *
749
         * @param callable(TSource, TIteratorKey): iterable<TCollectionKey, TCollection> $collectionSelector
750
         * @param null|callable(TSource, TCollection, TIteratorKey, TCollectionKey): TResult $resultSelector
751
         *
752
         * @return GenericKeyedEnumerable<TCollectionKey, TResult>
753
         */
754
        public function selectMany(callable $collectionSelector, ?callable $resultSelector = null): GenericKeyedEnumerable
755
        {
756
                $resultSelector ??= static fn (mixed $element, mixed $collectionElement, mixed $elementKey, mixed $collectionElementKey): mixed => $collectionElement;
11✔
757
                /** @var callable(TSource, TCollection, TIteratorKey, TCollectionKey): TResult $resultSelector */
758

759
                return new KeyedEnumerable(function () use ($collectionSelector, $resultSelector) {
11✔
760
                        /**
761
                         * @var TIteratorKey $elementKey
762
                         */
763
                        foreach ($this->getIterator() as $elementKey => $element) {
11✔
764
                                foreach ($collectionSelector($element, $elementKey) as $collectionElementKey => $collectionElement) {
11✔
765
                                        yield $collectionElementKey => $resultSelector($element, $collectionElement, $elementKey, $collectionElementKey);
11✔
766
                                }
767
                        }
768
                });
11✔
769
        }
770

771
        public function sequenceEqual(GenericKeyedEnumerable $other, ?callable $comparer = null): bool
772
        {
773
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
774

775
                $otherIterator = $other->getIterator();
1✔
776
                if (!($otherIterator instanceof Iterator)) {
1✔
777
                        $otherIterator = new IteratorIterator($otherIterator);
×
778
                }
779
                /** @var Iterator<TIteratorKey, TSource> $otherIterator */
780
                $iterator = $this->getIterator();
1✔
781
                if (!($iterator instanceof Iterator)) {
1✔
782
                        $iterator = new IteratorIterator($iterator);
×
783
                }
784

785
                /** @var callable(TSource, TSource, TIteratorKey, TIteratorKey): bool $comparer */
786
                $mit = new ParallelIterator(ParallelIterator::MIT_KEYS_NUMERIC | ParallelIterator::MIT_NEED_ANY);
1✔
787
                $mit->attachIterator($iterator);
1✔
788
                $mit->attachIterator($otherIterator);
1✔
789

790
                foreach ($mit as $keys => $values) {
1✔
791
                        /**
792
                         * @var array{TSource, TSource} $values
793
                         * @var array{TIteratorKey, TIteratorKey} $keys
794
                         */
795
                        if (!$comparer($values[0], $values[1], $keys[0], $keys[1])) {
1✔
796
                                return false;
1✔
797
                        }
798
                }
799

800
                return true;
1✔
801
        }
802

803
        public function single(?callable $predicate = null): mixed
804
        {
805
                $matched = false;
3✔
806
                $returnElement = null;
3✔
807

808
                foreach ($this->getIterator() as $elementKey => $element) {
3✔
809
                        if ($predicate === null || $predicate($element, $elementKey)) {
2✔
810
                                if ($matched) {
2✔
811
                                        throw new AmbiguousMatchException();
1✔
812
                                }
813

814
                                $matched = true;
2✔
815
                                $returnElement = $element;
2✔
816
                        }
817
                }
818

819
                if (!$matched) {
2✔
820
                        throw new EmptySequenceException();
1✔
821
                }
822

823
                return $returnElement;
1✔
824
        }
825

826
        public function singlePair(?callable $predicate = null): GenericKeyValuePair
827
        {
828
                $matched = false;
1✔
829
                $returnKey = null;
1✔
830
                $returnElement = null;
1✔
831

832
                foreach ($this->getIterator() as $elementKey => $element) {
1✔
833
                        if ($predicate === null || $predicate($element, $elementKey)) {
1✔
834
                                if ($matched) {
1✔
835
                                        throw new AmbiguousMatchException();
×
836
                                }
837

838
                                $matched = true;
1✔
839
                                $returnKey = $elementKey;
1✔
840
                                $returnElement = $element;
1✔
841
                        }
842
                }
843

844
                if (!$matched) {
1✔
845
                        throw new EmptySequenceException();
×
846
                }
847

848
                return new KeyValuePair($returnKey, $returnElement);
1✔
849
        }
850

851
        /**
852
         * @param TSource $default
853
         * @param null|callable(TSource, TIteratorKey): bool $predicate
854
         *
855
         * @return TSource
856
         */
857
        public function singleOrDefault(mixed $default, ?callable $predicate = null): mixed
858
        {
859
                $matched = false;
2✔
860
                $returnElement = null;
2✔
861

862
                foreach ($this->getIterator() as $elementKey => $element) {
2✔
863
                        if ($predicate === null || $predicate($element, $elementKey)) {
2✔
864
                                if ($matched) {
2✔
865
                                        throw new AmbiguousMatchException();
1✔
866
                                }
867

868
                                $matched = true;
2✔
869
                                $returnElement = $element;
2✔
870
                        }
871
                }
872

873
                return $matched ? $returnElement : $default;
1✔
874
        }
875

876
        public function skip(int $count): GenericKeyedEnumerable
877
        {
878
                $iterator = $this->getIterator();
1✔
879
                if (!($iterator instanceof Iterator)) {
1✔
880
                        $iterator = new IteratorIterator($iterator);
×
881
                }
882

883
                return new KeyedEnumerable(new LimitIterator($iterator, $count));
1✔
884
        }
885

886
        public function skipLast(int $count): GenericKeyedEnumerable
887
        {
888
                if ($count <= 0) {
2✔
889
                        throw new InvalidArgumentException('Count must be greater than zero');
1✔
890
                }
891

892
                $iterator = $this->getIterator();
2✔
893
                if (!($iterator instanceof Iterator)) {
2✔
894
                        $iterator = new IteratorIterator($iterator);
×
895
                }
896
                $cachedIterator = new CachingIterator($iterator, CachingIterator::FULL_CACHE);
2✔
897
                $cachedIterator->rewind();
2✔
898
                while ($cachedIterator->valid()) {
2✔
899
                        $cachedIterator->next();
1✔
900
                }
901

902
                $size = count($cachedIterator);
2✔
903
                $offset = $size - $count;
2✔
904
                if ($offset > 0) {
2✔
905
                        $iterator = new LimitIterator($cachedIterator, 0, $offset);
1✔
906
                } else {
907
                        $iterator = new EmptyIterator();
2✔
908
                }
909

910
                return new KeyedEnumerable($iterator);
2✔
911
        }
912

913
        /**
914
         * @param callable(TSource, TIteratorKey): bool $predicate
915
         *
916
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
917
         */
918
        public function skipWhile(callable $predicate): GenericKeyedEnumerable
919
        {
920
                /** @var Iterator<TIteratorKey, TSource> $iterator */
921
                $iterator = $this->getIterator();
1✔
922
                if (!($iterator instanceof Iterator)) {
1✔
923
                        $iterator = new IteratorIterator($iterator);
×
924
                }
925

926
                $whileIterator = new WhileIterator($iterator, $predicate(...));
1✔
927
                $whileIterator->rewind();
1✔
928
                while ($whileIterator->valid()) {
1✔
929
                        $whileIterator->next();
1✔
930
                }
931

932
                return new KeyedEnumerable(new NoRewindIterator($iterator));
1✔
933
        }
934

935
        /**
936
         * @param callable(TSource, TIteratorKey): numeric $selector
937
         *
938
         * @return numeric
939
         */
940
        public function sum(callable $selector): int|float|string
941
        {
942
                /** @var numeric */
943
                return $this->aggregate(static function (mixed $accumulator, mixed $element, mixed $elementKey) use ($selector) {
1✔
944
                        /**
945
                         * @var numeric $accumulator
946
                         * @var TSource $element
947
                         * @var TIteratorKey $elementKey
948
                         */
949
                        return $accumulator + $selector($element, $elementKey);
1✔
950
                }, 0);
1✔
951
        }
952

953
        public function take(int $count): GenericKeyedEnumerable
954
        {
955
                $iterator = $this->getIterator();
1✔
956
                if (!($iterator instanceof Iterator)) {
1✔
957
                        $iterator = new IteratorIterator($iterator);
×
958
                }
959

960
                return new KeyedEnumerable(new LimitIterator($iterator, 0, $count));
1✔
961
        }
962

963
        public function takeLast(int $count): GenericKeyedEnumerable
964
        {
965
                $iterator = $this->getIterator();
3✔
966
                if (!($iterator instanceof Iterator)) {
3✔
967
                        $iterator = new IteratorIterator($iterator);
×
968
                }
969
                $cachedIterator = new CachingIterator($iterator, CachingIterator::FULL_CACHE);
3✔
970
                $cachedIterator->rewind();
3✔
971
                while ($cachedIterator->valid()) {
3✔
972
                        $cachedIterator->next();
2✔
973
                }
974

975
                $size = count($cachedIterator);
3✔
976
                $offset = $size - $count;
3✔
977
                if ($offset < 0) {
3✔
978
                        return new KeyedEnumerable(new EmptyIterator());
1✔
979
                }
980

981
                return new KeyedEnumerable(new LimitIterator($cachedIterator, $offset));
2✔
982
        }
983

984
        /**
985
         * @param callable(TSource, TIteratorKey): bool $predicate
986
         *
987
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
988
         */
989
        public function takeWhile(callable $predicate): GenericKeyedEnumerable
990
        {
991
                $iterator = $this->getIterator();
1✔
992
                if (!($iterator instanceof Iterator)) {
1✔
993
                        $iterator = new IteratorIterator($iterator);
×
994
                }
995

996
                return new KeyedEnumerable(new WhileIterator($iterator, $predicate(...)));
1✔
997
        }
998

999
        /**
1000
         * @return list<TSource>
1001
         */
1002
        public function toList(): array
1003
        {
1004
                $list = [];
56✔
1005

1006
                /** @var TSource $element */
1007
                foreach ($this->getIterator() as $element) {
56✔
1008
                        $list[] = $element;
32✔
1009
                }
1010

1011
                return $list;
56✔
1012
        }
1013

1014
        /**
1015
         * @return ArrayList<TSource>
1016
         */
1017
        public function toArrayList(): ArrayList
1018
        {
1019
                return new ArrayList($this->toList());
1✔
1020
        }
1021

1022
        /**
1023
         * @return ArrayMap<TIteratorKey, TSource>
1024
         */
1025
        public function toArrayMap(): ArrayMap
1026
        {
1027
                return new ArrayMap($this->toArray());
×
1028
        }
1029

1030
        /**
1031
         * @template TObjectKey of object
1032
         *
1033
         * @return ObjectMap<TObjectKey, TSource>
1034
         */
1035
        public function toObjectMap(): ObjectMap
1036
        {
1037
                $map = new ObjectMap();
×
1038

1039
                foreach ($this->getIterator() as $key => $value) {
×
1040
                        $map->put($key, $value);
×
1041
                }
1042

1043
                return $map;
×
1044
        }
1045

1046
        public function toArray(?callable $keySelector = null): array
1047
        {
1048
                $keySelector ??= static fn (mixed $key, mixed $value): mixed => $key;
142✔
1049

1050
                $array = [];
142✔
1051

1052
                foreach ($this->getIterator() as $elementKey => $element) {
142✔
1053
                        $key = $keySelector($elementKey, $element);
120✔
1054

1055
                        if ($key instanceof Stringable) {
120✔
1056
                                $key = $key->__toString();
1✔
1057
                        } elseif (!is_scalar($key)) {
120✔
1058
                                throw new OutOfBoundsException('Invalid array key: ' . get_debug_type($key));
1✔
1059
                        }
1060

1061
                        /** @var array-key $key */
1062
                        $array[$key] = $element;
120✔
1063
                }
1064

1065
                return $array;
142✔
1066
        }
1067

1068
        /**
1069
         * @throws JsonException
1070
         */
1071
        public function toJson(
1072
                #[ExpectedValues(flags: [
1073
                        JSON_FORCE_OBJECT,
1074
                        JSON_HEX_QUOT,
1075
                        JSON_HEX_TAG,
1076
                        JSON_HEX_AMP,
1077
                        JSON_HEX_APOS,
1078
                        JSON_INVALID_UTF8_IGNORE,
1079
                        JSON_INVALID_UTF8_SUBSTITUTE,
1080
                        JSON_NUMERIC_CHECK,
1081
                        JSON_PARTIAL_OUTPUT_ON_ERROR,
1082
                        JSON_PRESERVE_ZERO_FRACTION,
1083
                        JSON_PRETTY_PRINT,
1084
                        JSON_UNESCAPED_LINE_TERMINATORS,
1085
                        JSON_UNESCAPED_SLASHES,
1086
                        JSON_UNESCAPED_UNICODE,
1087
                        JSON_THROW_ON_ERROR,
1088
                ])] int $flags = 0,
1089
                int $depth = 512,
1090
        ): string {
1091
                return json_encode($this->toArray(), $flags | JSON_THROW_ON_ERROR, $depth);
1✔
1092
        }
1093

1094
        public function groupByKey(?callable $keySelector = null): array
1095
        {
1096
                $keySelector ??= static fn (mixed $key, mixed $value): mixed => $key;
2✔
1097

1098
                $array = [];
2✔
1099

1100
                /**
1101
                 * @var TIteratorKey $elementKey
1102
                 * @var TSource $element
1103
                 */
1104
                foreach ($this->getIterator() as $elementKey => $element) {
2✔
1105
                        $key = $keySelector($elementKey, $element);
2✔
1106

1107
                        if ($key instanceof Stringable) {
2✔
1108
                                $key = (string) $key;
1✔
1109
                        }
1110

1111
                        if (!is_scalar($key)) {
2✔
1112
                                throw new OutOfBoundsException('Invalid array key: ' . get_debug_type($key));
1✔
1113
                        }
1114

1115
                        /**
1116
                         * @var array-key $key
1117
                         */
1118
                        $array[$key][] = $element;
2✔
1119
                }
1120

1121
                return $array;
2✔
1122
        }
1123

1124
        public function keys(): GenericEnumerable
1125
        {
1126
                $iterator = $this->getIterator();
8✔
1127
                if (!($iterator instanceof Iterator)) {
8✔
1128
                        $iterator = new IteratorIterator($iterator);
×
1129
                }
1130

1131
                return new Enumerable(new FlipIterator($iterator));
8✔
1132
        }
1133

1134
        public function values(): GenericEnumerable
1135
        {
1136
                return new Enumerable($this->getIterator());
10✔
1137
        }
1138

1139
        /**
1140
         * @param GenericKeyedEnumerable<TIteratorKey, TSource> $other
1141
         * @param null|callable(TSource, TSource): bool $comparer
1142
         *
1143
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
1144
         */
1145
        public function union(GenericKeyedEnumerable $other, ?callable $comparer = null): GenericKeyedEnumerable
1146
        {
1147
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
1148
                $identity = static fn (mixed $o): mixed => $o;
1✔
1149

1150
                /**
1151
                 * @var callable(TSource, TSource): bool $comparer
1152
                 * @var callable(TSource): TSource $identity
1153
                 */
1154
                return $this->unionBy($other, $identity, $comparer);
1✔
1155
        }
1156

1157
        /**
1158
         * @template TCompareKey
1159
         *
1160
         * @param GenericKeyedEnumerable<TIteratorKey, TSource> $other
1161
         * @param callable(TSource): TCompareKey $keySelector
1162
         * @param null|callable(TCompareKey, TCompareKey): bool $comparer
1163
         *
1164
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
1165
         */
1166
        public function unionBy(GenericKeyedEnumerable $other, callable $keySelector, ?callable $comparer = null): GenericKeyedEnumerable
1167
        {
1168
                $comparer ??= DefaultEqualityComparer::same(...);
2✔
1169

1170
                $iterator = $this->getIterator();
2✔
1171
                if (!($iterator instanceof Iterator)) {
2✔
1172
                        $iterator = new IteratorIterator($iterator);
×
1173
                }
1174

1175
                $otherIterator = $other->getIterator();
2✔
1176
                if (!($otherIterator instanceof Iterator)) {
2✔
1177
                        $otherIterator = new IteratorIterator($otherIterator);
×
1178
                }
1179

1180
                $append = new AppendIterator();
2✔
1181
                $append->append($iterator);
2✔
1182
                $append->append($otherIterator);
2✔
1183

1184
                /**
1185
                 * @var Closure(TSource): TCompareKey $keySelector
1186
                 * @var Closure(TCompareKey, TCompareKey): bool $comparer
1187
                 */
1188
                return new KeyedEnumerable(new UniqueByIterator($append, $keySelector(...), $comparer(...)));
2✔
1189
        }
1190

1191
        /**
1192
         * @param null|callable(TSource, TSource): bool $comparer
1193
         *
1194
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
1195
         */
1196
        public function unique(?callable $comparer = null): GenericKeyedEnumerable
1197
        {
1198
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
1199
                $identity = static fn (mixed $o): mixed => $o;
1✔
1200

1201
                /**
1202
                 * @var callable(TSource, TSource): bool $comparer
1203
                 * @var callable(TSource): TSource $identity
1204
                 */
1205
                return $this->uniqueBy($identity, $comparer);
1✔
1206
        }
1207

1208
        /**
1209
         * @template TCompareKey
1210
         *
1211
         * @param callable(TSource): TCompareKey $keySelector
1212
         * @param null|callable(TCompareKey, TCompareKey): bool $comparer
1213
         *
1214
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
1215
         */
1216
        public function uniqueBy(callable $keySelector, ?callable $comparer = null): GenericKeyedEnumerable
1217
        {
1218
                $comparer ??= DefaultEqualityComparer::same(...);
1✔
1219

1220
                $iterator = $this->getIterator();
1✔
1221
                if (!($iterator instanceof Iterator)) {
1✔
1222
                        $iterator = new IteratorIterator($iterator);
×
1223
                }
1224

1225
                /**
1226
                 * @var Closure(TSource): TCompareKey $keySelector
1227
                 * @var Closure(TCompareKey, TCompareKey): bool $comparer
1228
                 */
1229
                return new KeyedEnumerable(new UniqueByIterator($iterator, $keySelector(...), $comparer(...)));
1✔
1230
        }
1231

1232
        /**
1233
         * @param callable(TSource, TIteratorKey, Iterator<TIteratorKey, TSource>): bool $predicate
1234
         *
1235
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
1236
         */
1237
        public function where(callable $predicate): GenericKeyedEnumerable
1238
        {
1239
                $iterator = $this->getIterator();
13✔
1240
                if (!($iterator instanceof Iterator)) {
13✔
1241
                        $iterator = new IteratorIterator($iterator);
×
1242
                }
1243

1244
                return new KeyedEnumerable(new CallbackFilterIterator($iterator, $predicate(...)));
13✔
1245
        }
1246

1247
        /**
1248
         * @param callable(TIteratorKey, TSource, Iterator<TSource, TIteratorKey>): bool $predicate
1249
         *
1250
         * @return GenericKeyedEnumerable<TIteratorKey, TSource>
1251
         */
1252
        public function whereKey(callable $predicate): GenericKeyedEnumerable
1253
        {
1254
                $iterator = $this->getIterator();
1✔
1255
                if (!($iterator instanceof Iterator)) {
1✔
1256
                        $iterator = new IteratorIterator($iterator);
×
1257
                }
1258

1259
                return new KeyedEnumerable(new FlipIterator(new CallbackFilterIterator(new FlipIterator($iterator), $predicate(...))));
1✔
1260
        }
1261

1262
        /**
1263
         * @template TOther
1264
         * @template TOtherIteratorKey
1265
         * @template TResult
1266
         * @template TResultKey
1267
         *
1268
         * @param GenericKeyedEnumerable<TOtherIteratorKey, TOther> $other
1269
         * @param null|callable(TSource, TOther): TResult $resultSelector
1270
         * @param null|callable(TIteratorKey, TOtherIteratorKey): TResultKey $keySelector
1271
         *
1272
         * @return GenericKeyedEnumerable<TResultKey, TResult>
1273
         */
1274
        public function zip(GenericKeyedEnumerable $other, ?callable $resultSelector = null, ?callable $keySelector = null): GenericKeyedEnumerable
1275
        {
1276
                $resultSelector ??= static fn (mixed $a, mixed $b): array => [$a, $b];
1✔
1277
                $keySelector ??= static fn (mixed $a, mixed $b): mixed => $a;
1✔
1278

1279
                $iterator = $this->getIterator();
1✔
1280
                if (!($iterator instanceof Iterator)) {
1✔
1281
                        $iterator = new IteratorIterator($iterator);
×
1282
                }
1283

1284
                $otherIterator = $other->getIterator();
1✔
1285
                if (!($otherIterator instanceof Iterator)) {
1✔
1286
                        $otherIterator = new IteratorIterator($otherIterator);
×
1287
                }
1288

1289
                $mit = new ParallelIterator(ParallelIterator::MIT_KEYS_NUMERIC | ParallelIterator::MIT_NEED_ALL);
1✔
1290
                $mit->attachIterator($iterator);
1✔
1291
                $mit->attachIterator($otherIterator);
1✔
1292
                /** @var ParallelIterator $mit */
1293

1294
                /** @var GenericKeyedEnumerable<TResultKey, TResult> */
1295
                return new KeyedEnumerable(
1✔
1296
                        /** @var SelectIterator<TResultKey, TResult> */
1297
                        new SelectIterator(
1✔
1298
                                /** @var KeySelectIterator<TResultKey, array{TSource, TOther}> */
1299
                                new KeySelectIterator(
1✔
1300
                                        $mit,
1✔
1301
                                        static function (mixed $keys) use ($keySelector): mixed {
1✔
1302
                                                /** @var array{TIteratorKey, TOtherIteratorKey} $keys */
1303
                                                return $keySelector($keys[0], $keys[1]);
1✔
1304
                                        },
1✔
1305
                                ),
1✔
1306
                                static function (mixed $values) use ($resultSelector): array {
1✔
1307
                                        /** @var array{TSource, TOther} $values */
1308
                                        return $resultSelector($values[0], $values[1]);
1✔
1309
                                },
1✔
1310
                        ),
1✔
1311
                );
1✔
1312
        }
1313
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc