Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.41% covered (success)
97.41%
188 / 193
94.29% covered (success)
94.29%
33 / 35
CRAP
0.00% covered (danger)
0.00%
0 / 1
Collections
97.41% covered (success)
97.41%
188 / 193
94.29% covered (success)
94.29%
33 / 35
97
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 wrap
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIterator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 from
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 range
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 repeat
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 empty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toArray
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toList
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 toDictionary
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
5
 where
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 select
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 selectMany
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 concat
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 prepend
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 append
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 any
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 all
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 count
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
 first
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 firstOr
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 last
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
6.02
 lastOr
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
6
 single
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
8
 singleOr
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
8
 skip
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 skipWhile
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 take
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 takeWhile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 reverse
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 aggregate
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 max
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 min
60.00% covered (warning)
60.00%
6 / 10
0.00% covered (danger)
0.00%
0 / 1
8.30
 zip
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\Core\Collection;
6
7use AppendIterator;
8use ArrayIterator;
9use CallbackFilterIterator;
10use Countable;
11use EmptyIterator;
12use Iterator;
13use IteratorAggregate;
14use LimitIterator;
15use Traversable;
16use PeServer\Core\Collection\Arr;
17use PeServer\Core\Throws\ArgumentException;
18use PeServer\Core\Throws\InvalidOperationException;
19use PeServer\Core\TypeUtility;
20
21/**
22 * イテレータを使用したコレクション処理(LINQしたいのだ)。
23 *
24 * 実行速度ではなく、開発効率を目標としている。
25 *
26 * @template TKey of array-key
27 * @template TValue
28 * @phpstan-type PredicateAlias callable(TValue,TKey):(bool)
29 * @implements IteratorAggregate<TKey,TValue>
30 */
31class Collections implements IteratorAggregate
32{
33    /**
34     * 生成。
35     *
36     * @param Iterator $iterator
37     * @phpstan-param Iterator<TKey,TValue> $iterator
38     */
39    private function __construct(
40        private Iterator $iterator
41    ) {
42    }
43
44    #region function
45
46    /**
47     * イテレータから生成。
48     *
49     * @template TCreateKey of array-key
50     * @template TCreateValue
51     * @param Iterator $iterator
52     * @phpstan-param Iterator<TCreateKey,TCreateValue> $iterator
53     * @return self
54     * @phpstan-return self<TCreateKey,TCreateValue>
55     */
56    private static function create(Iterator $iterator): self
57    {
58        return new self($iterator);
59    }
60
61    /**
62     * ジェネレータから生成。
63     *
64     * @template TWrapKey of array-key
65     * @template TWrapValue
66     * @param callable $generatorFactory
67     * @phpstan-param callable():\Generator<TWrapKey,TWrapValue> $generatorFactory
68     * @return self
69     * @phpstan-return self<TWrapKey,TWrapValue>
70     */
71    private static function wrap(callable $generatorFactory): self
72    {
73        return self::create(TraverseUtility::toIterator($generatorFactory));
74    }
75
76    /**
77     * @phpstan-return Traversable<TKey,TValue>
78     */
79    public function getIterator(): Traversable
80    {
81        return $this->iterator;
82    }
83
84    #region 開始
85
86    /**
87     * 配列からコレクション生成。
88     *
89     * @template TFromKey of array-key
90     * @template TFromValue
91     * @param Traversable|array<mixed>|callable $sequence
92     * @phpstan-param Traversable<TFromKey,TFromValue>|array<TFromKey,TFromValue>|callable():(\Generator<TFromKey,TFromValue>) $sequence
93     * @return self
94     * @phpstan-return self<TFromKey,TFromValue>
95     */
96    public static function from(Traversable|array|callable $sequence): self
97    {
98        return self::create(TraverseUtility::toIterator($sequence));
99    }
100
101    /**
102     * 指定した範囲内の整数からコレクション生成。
103     *
104     * @param int $start 開始。
105     * @param int $count 件数。
106     * @phpstan-param non-negative-int $count
107     * @return self
108     * @phpstan-return self<non-negative-int,int>
109     */
110    public static function range(int $start, int $count): self
111    {
112        $iterator = new RangeIterator($start, $count);
113        /** @phpstan-var self<non-negative-int,int> */
114        return self::create($iterator);
115    }
116
117    /**
118     * 繰り返されるコレクション生成。
119     *
120     * @template TRepeatValue
121     * @param mixed $value 値。
122     * @phpstan-param TRepeatValue $value
123     * @param int $count 件数。
124     * @phpstan-param non-negative-int $count
125     * @return self
126     * @phpstan-return self<non-negative-int,TRepeatValue>
127     */
128    public static function repeat(mixed $value, int $count): self
129    {
130        $iterator = new RepeatIterator($value, $count);
131        /** @phpstan-var self<non-negative-int,TRepeatValue> */
132        return self::create($iterator);
133    }
134
135    /**
136     * 空のコレクション。
137     *
138     * @return self
139     * @phpstan-return self<TKey,TValue>
140     */
141    public static function empty(): self
142    {
143        return self::create(new EmptyIterator());
144    }
145
146    #endregion
147
148    #region 実体化
149
150    /**
151     * 配列実体化。
152     *
153     * @return array<mixed>
154     * @phpstan-return array<array-key,TValue>
155     */
156    public function toArray(): array
157    {
158        return TraverseUtility::toArray($this->iterator, false);
159    }
160
161    /**
162     * リスト実体化。
163     *
164     * `Enumerable.ToList<TSource>` 的な。
165     *
166     * @param class-string|TypeUtility::TYPE_* $type
167     * @return Vector
168     * @phpstan-return Vector<TValue>
169     */
170    public function toList(string $type = TypeUtility::TYPE_NULL): Vector
171    {
172        $array = self::toArray();
173        if (Arr::isNullOrEmpty($array)) {
174            return Vector::empty($type);
175        }
176        /** @phpstan-var non-empty-array<TValue> $array */
177
178        return Vector::create($array, true);
179    }
180
181    /**
182     * 辞書実体化。
183     *
184     * @template TResult
185     * @param callable $keyFactory 第1引数:値, 第1引数:キー(文字列変換済み), 戻り値:キー(文字列)。
186     * @phpstan-param callable(TValue,TKey):string $keyFactory
187     * @param callable $valueFactory 第1引数:値, 第1引数:キー(文字列変換済み), 戻り値:値。
188     * @phpstan-param callable(TValue,TKey):TResult $valueFactory
189     * @param class-string|TypeUtility::TYPE_* $type
190     * @return Dictionary
191     * @phpstan-return Dictionary<TResult>
192     */
193    public function toDictionary(callable $keyFactory, callable $valueFactory, string $type = TypeUtility::TYPE_NULL): Dictionary
194    {
195        /** @phpstan-var array<string,TResult> */
196        $buffer = [];
197
198        foreach ($this->iterator as $key => $value) {
199            $convertedKey = $key;
200            if (!is_string($key)) {
201                $convertedKey = TypeUtility::toString($key);
202            }
203
204            $k = call_user_func($keyFactory, $value, $convertedKey);
205            $v = call_user_func($valueFactory, $value, $convertedKey);
206
207            if (isset($buffer[$k])) {
208                throw new ArgumentException($k);
209            }
210
211            $buffer[$k] = $v;
212        }
213
214        if (Arr::isNullOrEmpty($buffer)) {
215            return Dictionary::empty($type);
216        }
217        /** @phpstan-var non-empty-array<string,TResult> $buffer */
218
219        return Dictionary::create($buffer);
220    }
221
222    #endregion
223
224    #region 処理
225
226    /**
227     * [遅延] フィルタリング
228     *
229     * @param callable $callback 値, キー: 条件を満たすか
230     * @phpstan-param PredicateAlias $callback
231     * @return self
232     * @phpstan-return self<TKey,TValue>
233     */
234    public function where(callable $callback): self
235    {
236        $iterator = new CallbackFilterIterator($this->iterator, $callback);
237        return self::create($iterator);
238    }
239
240    /**
241     * [遅延] 射影。
242     *
243     * @template TResult
244     * @param callable $callback
245     * @phpstan-param callable(TValue,TKey):(TResult) $callback
246     * @return self
247     * @phpstan-return self<TKey,TResult>
248     */
249    public function select(callable $callback): self
250    {
251        $selectIterator = new SelectIterator($this->iterator, $callback);
252        return self::create($selectIterator);
253    }
254
255    /**
256     * [遅延] 射影-平坦化。
257     *
258     * @template TResult
259     * @param callable $callback
260     * @phpstan-param callable(TValue,TKey):(TResult) $callback
261     * @return self
262     * @phpstan-return self<TKey,TResult>
263     */
264    public function selectMany(callable $callback): self
265    {
266        //@phpstan-ignore-next-line
267        $selectIterator = new SelectManyIterator($this->iterator, $callback);
268        return new self($selectIterator);
269    }
270
271
272    /**
273     * [遅延] 末尾に連結。
274     *
275     * @param Traversable|array<mixed>|callable $sequence
276     * @phpstan-param Traversable<TKey,TValue>|array<TKey,TValue>|callable():\Generator<TKey,TValue> $sequence
277     * @return self
278     * @phpstan-return self<TKey,TValue>
279     */
280    public function concat(Traversable|array|callable $sequence): self
281    {
282        $sequenceIterator = TraverseUtility::toIterator($sequence);
283
284        /** @phpstan-var AppendIterator<TKey,TValue,Iterator<TKey,TValue>> */
285        $appendIterator = new AppendIterator();
286        $appendIterator->append($this->iterator);
287        $appendIterator->append($sequenceIterator);
288
289        return self::create($appendIterator);
290    }
291
292    /**
293     * [遅延] 要素を先頭追加。
294     *
295     * @param mixed $value
296     * @phpstan-param TValue $value
297     * @return self
298     * @phpstan-return self<TKey,TValue>
299     */
300    public function prepend(mixed $value): self
301    {
302        $valueIterator = TraverseUtility::toIterator([$value]);
303
304        /** @phpstan-var AppendIterator<TKey,TValue,Iterator<TKey,TValue>> */
305        $appendIterator = new AppendIterator();
306        $appendIterator->append($valueIterator);
307        $appendIterator->append($this->iterator);
308
309        return self::create($appendIterator);
310    }
311
312    /**
313     * [遅延] 要素を末尾追加。
314     *
315     * @param mixed $value
316     * @phpstan-param TValue $value
317     * @return self
318     * @phpstan-return self<TKey,TValue>
319     */
320    public function append(mixed $value): self
321    {
322        $valueIterator = TraverseUtility::toIterator([$value]);
323
324        /** @phpstan-var AppendIterator<TKey,TValue,Iterator<TKey,TValue>> */
325        $appendIterator = new AppendIterator();
326        $appendIterator->append($this->iterator);
327        $appendIterator->append($valueIterator);
328
329        return self::create($appendIterator);
330    }
331
332    /**
333     * [即時] 要素が含まれているか。
334     *
335     * @param callable|null $callback 非nullの場合条件指定。
336     * @phpstan-param PredicateAlias|null $callback
337     * @return boolean
338     */
339    public function any(?callable $callback = null): bool
340    {
341        if ($callback === null) {
342            $this->iterator->rewind();
343            return $this->iterator->valid();
344        }
345
346        foreach ($this->iterator as $key => $value) {
347            if ($callback($value, $key)) {
348                return true;
349            }
350        }
351
352        return false;
353    }
354
355    /**
356     * [即時] 全ての要素が条件を満たすか。
357     *
358     * @param callable $callback
359     * @phpstan-param PredicateAlias $callback
360     * @return boolean
361     */
362    public function all(callable $callback): bool
363    {
364        foreach ($this->iterator as $key => $value) {
365            if (!$callback($value, $key)) {
366                return false;
367            }
368        }
369
370        return true;
371    }
372
373    /**
374     * [即時] 件数を取得。
375     *
376     * @param callable|null $callback 非nullの場合条件指定。
377     * @phpstan-param PredicateAlias|null $callback
378     * @return int
379     */
380    public function count(callable $callback = null): int
381    {
382        if ($callback === null) {
383            if ($this->iterator instanceof Countable) {
384                return $this->iterator->count();
385            }
386
387            return iterator_count($this->iterator);
388        }
389
390        $count = 0;
391        $this->iterator->rewind();
392        while ($this->iterator->valid()) {
393            if ($callback($this->iterator->current(), $this->iterator->key())) {
394                $count += 1;
395            }
396            $this->iterator->next();
397        }
398
399        return $count;
400    }
401
402    /**
403     * [即時] 先頭要素を取得。
404     *
405     * @param callable|null $callback 非nullの場合条件指定。
406     * @phpstan-param PredicateAlias|null $callback
407     * @return mixed
408     * @phpstan-return TValue
409     * @throws InvalidOperationException 要素なし。
410     */
411    public function first(?callable $callback = null): mixed
412    {
413        if ($callback === null) {
414            foreach ($this->iterator as $key => $value) {
415                return $value;
416            }
417        } else {
418            foreach ($this->iterator as $key => $value) {
419                if ($callback($value, $key)) {
420                    return $value;
421                }
422            }
423        }
424
425        throw new InvalidOperationException();
426    }
427
428    /**
429     * [即時] 先頭要素を取得、存在しない場合は指定値を返す。
430     *
431     * @param mixed $notFound 存在しない場合の戻り値。
432     * @phpstan-param TValue $notFound
433     * @param callable|null $callback 非nullの場合条件指定。
434     * @phpstan-param PredicateAlias|null $callback
435     * @return mixed
436     * @phpstan-return TValue
437     */
438    public function firstOr(mixed $notFound, ?callable $callback = null): mixed
439    {
440        if ($callback === null) {
441            foreach ($this->iterator as $key => $value) {
442                return $value;
443            }
444        } else {
445            foreach ($this->iterator as $key => $value) {
446                if ($callback($value, $key)) {
447                    return $value;
448                }
449            }
450        }
451
452        return $notFound;
453    }
454
455    /**
456     * [即時] 終端要素を取得。
457     *
458     * @param callable|null $callback 非nullの場合条件指定。
459     * @phpstan-param PredicateAlias|null $callback
460     * @return mixed
461     * @phpstan-return TValue
462     * @throws InvalidOperationException 要素なし。
463     */
464    public function last(?callable $callback = null): mixed
465    {
466        $isFound = false;
467        /** @phpstan-var TValue */
468        $current = null;
469
470        if ($callback === null) {
471            foreach ($this->iterator as $key => $value) {
472                $isFound = true;
473                $current = $value;
474            }
475        } else {
476            foreach ($this->iterator as $key => $value) {
477                if ($callback($value, $key)) {
478                    $isFound = true;
479                    $current = $value;
480                }
481            }
482        }
483
484        if ($isFound) {
485            return $current;
486        }
487
488        throw new InvalidOperationException();
489    }
490
491    /**
492     * [即時] 終端要素を取得、存在しない場合は指定値を返す。
493     *
494     * @param mixed $notFound 存在しない場合の戻り値。
495     * @phpstan-param TValue $notFound
496     * @param callable|null $callback 非nullの場合条件指定。
497     * @phpstan-param PredicateAlias|null $callback
498     * @return mixed
499     * @phpstan-return TValue
500     * @throws InvalidOperationException 要素なし。
501     */
502    public function lastOr(mixed $notFound, ?callable $callback = null): mixed
503    {
504        $isFound = false;
505        /** @phpstan-var TValue */
506        $current = null;
507
508        if ($callback === null) {
509            foreach ($this->iterator as $key => $value) {
510                $isFound = true;
511                $current = $value;
512            }
513        } else {
514            foreach ($this->iterator as $key => $value) {
515                if ($callback($value, $key)) {
516                    $isFound = true;
517                    $current = $value;
518                }
519            }
520        }
521
522        if ($isFound) {
523            return $current;
524        }
525
526        return $notFound;
527    }
528
529    /**
530     * [即時] 単独の要素取得。
531     *
532     * @param callable|null $callback 非nullの場合条件指定。
533     * @phpstan-param PredicateAlias|null $callback
534     * @return mixed
535     * @phpstan-return TValue
536     * @throws InvalidOperationException 要素なし/複数あり。
537     */
538    public function single(?callable $callback = null): mixed
539    {
540        $isFound = false;
541        /** @phpstan-var TValue */
542        $current = null;
543
544        if ($callback === null) {
545            foreach ($this->iterator as $key => $value) {
546                if ($isFound) {
547                    throw new InvalidOperationException();
548                }
549                $isFound = true;
550                $current = $value;
551            }
552        } else {
553            foreach ($this->iterator as $key => $value) {
554                if ($callback($value, $key)) {
555                    if ($isFound) {
556                        throw new InvalidOperationException();
557                    }
558                    $isFound = true;
559                    $current = $value;
560                }
561            }
562        }
563
564        if ($isFound) {
565            return $current;
566        }
567
568        throw new InvalidOperationException();
569    }
570
571    /**
572     * [即時] 単独の要素取得、存在しない場合は指定値を返す。
573     *
574     * @param mixed $notFound 存在しない場合の戻り値。
575     * @phpstan-param TValue $notFound
576     * @param callable|null $callback 非nullの場合条件指定。
577     * @phpstan-param PredicateAlias|null $callback
578     * @return mixed
579     * @phpstan-return TValue
580     * @throws InvalidOperationException 複数あり。
581     */
582    public function singleOr(mixed $notFound, ?callable $callback = null): mixed
583    {
584        $isFound = false;
585        /** @phpstan-var TValue */
586        $current = null;
587
588        if ($callback === null) {
589            foreach ($this->iterator as $key => $value) {
590                if ($isFound) {
591                    throw new InvalidOperationException();
592                }
593                $isFound = true;
594                $current = $value;
595            }
596        } else {
597            foreach ($this->iterator as $key => $value) {
598                if ($callback($value, $key)) {
599                    if ($isFound) {
600                        throw new InvalidOperationException();
601                    }
602                    $isFound = true;
603                    $current = $value;
604                }
605            }
606        }
607
608        if ($isFound) {
609            return $current;
610        }
611
612        return $notFound;
613    }
614
615    /**
616     * [遅延] 先頭から指定数をバイパス。
617     *
618     * @param int $skipCount
619     * @phpstan-param non-negative-int $skipCount
620     * @return self
621     * @phpstan-return self<TKey,TValue>
622     */
623    public function skip(int $skipCount): self
624    {
625        $limitIterator = new LimitIterator($this->iterator, $skipCount);
626        return self::create($limitIterator);
627    }
628
629    /**
630     * [遅延] 先頭から条件を満たす限りバイパス。
631     *
632     * @param callable $callback
633     * @phpstan-param PredicateAlias $callback
634     * @return self
635     * @phpstan-return self<TKey,TValue>
636     */
637    public function skipWhile(callable $callback): self
638    {
639        return self::wrap(function () use ($callback) {
640            $skipCount = 0;
641            foreach ($this->iterator as $key => $value) {
642                if (!$callback($value, $key)) {
643                    foreach ($this->skip($skipCount) as $key => $value) {
644                        yield $key => $value;
645                    }
646                }
647                $skipCount += 1;
648            }
649
650            return self::empty();
651        });
652    }
653
654    /**
655     * [遅延] 先頭から指定された件数を返却。
656     *
657     * @param int $takeCount
658     * @phpstan-param non-negative-int $takeCount
659     * @return self
660     * @phpstan-return self<TKey,TValue>
661     */
662    public function take(int $takeCount): self
663    {
664        return self::create(new TakeIterator($this->iterator, $takeCount));
665    }
666
667    /**
668     * [遅延] 先頭から条件を満たすデータを返却。
669     *
670     * @param callable $callback
671     * @phpstan-param PredicateAlias $callback
672     * @return self
673     * @phpstan-return self<TKey,TValue>
674     */
675    public function takeWhile(callable $callback): self
676    {
677        return self::create(new TakeWhileIterator($this->iterator, $callback));
678    }
679
680    /**
681     * [遅延] 反転。
682     *
683     * @return self
684     * @phpstan-return self<TKey,TValue>
685     */
686    public function reverse(): self
687    {
688        return self::wrap(function () {
689            $cache = [];
690            foreach ($this->iterator as $key => $value) {
691                $cache[] = [$key, $value];
692            }
693            $count = count($cache);
694            for ($i = $count - 1; $i >= 0; $i--) {
695                yield $cache[$i][0] => $cache[$i][1];
696            }
697        });
698    }
699
700    /**
701     * [即時] 集計。
702     *
703     * @param callable $callback
704     * @phpstan-param callable(TValue $result,TValue,TKey):(TValue) $callback
705     * @param mixed $initial
706     * @phpstan-param TValue $initial
707     * @phpstan-return TValue
708     */
709    public function aggregate(callable $callback, mixed $initial = 0): mixed
710    {
711        $result = $initial;
712
713        foreach ($this->iterator as $key => $value) {
714            $result = $callback($result, $value, $key);
715        }
716
717        return $result;
718    }
719
720    /**
721     * [即時] 最大データの取得。
722     *
723     * @param callable|null $callback
724     * @phpstan-param callable(TValue, TKey): TValue|null $callback
725     * @return mixed
726     * @phpstan-return TValue|null
727     */
728    public function max(callable $callback = null): mixed
729    {
730        /** @phpstan-var TValue|null */
731        $result = PHP_INT_MIN;
732
733        if ($callback === null) {
734            foreach ($this->iterator as $key => $value) {
735                if ($result < $value) {
736                    $result = $value;
737                }
738            }
739        } else {
740            foreach ($this->iterator as $key => $value) {
741                $ret = $callback($value, $key);
742                if ($result < $ret) {
743                    $result = $ret;
744                }
745            }
746        }
747
748        return $result;
749    }
750
751    /**
752     * [即時] 最小データの取得。
753     *
754     * @param callable|null $callback
755     * @phpstan-param callable(TValue, TKey): TValue|null $callback
756     * @return mixed
757     * @phpstan-return TValue|null
758     */
759    public function min(callable $callback = null): mixed
760    {
761        /** @phpstan-var TValue|null */
762        $result = PHP_INT_MAX;
763
764        if ($callback === null) {
765            foreach ($this->iterator as $key => $value) {
766                if ($value < $result) {
767                    $result = $value;
768                }
769            }
770        } else {
771            foreach ($this->iterator as $key => $value) {
772                $ret = $callback($value, $key);
773                if ($result < $ret) {
774                    $result = $ret;
775                }
776            }
777        }
778
779        return $result;
780    }
781
782    /**
783     * [遅延] zip
784     *
785     * @template TSequenceKey of array-key
786     * @template TSequenceValue
787     * @template TResult
788     * @param Traversable|array|callable $sequence
789     * @phpstan-param Traversable<TSequenceKey,TSequenceValue>|array<TSequenceKey,TSequenceValue>|callable():\Generator<TSequenceKey,TSequenceValue> $sequence
790     * @param callable $callback
791     * @phpstan-param callable(array{0:TValue,1:TSequenceValue},TKey):(TResult) $callback
792     * @return self
793     * @phpstan-return self<array-key,TResult>
794     */
795    public function zip(Traversable|array|callable $sequence, callable $callback): self
796    {
797        $sequenceIterator = TraverseUtility::toIterator($sequence);
798        $iterator = new ZipIterator($this->iterator, $sequenceIterator, $callback);
799        return self::create($iterator);
800    }
801
802    #endregion
803
804    #endregion
805}