Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
97.41% |
188 / 193 |
|
94.29% |
33 / 35 |
CRAP | |
0.00% |
0 / 1 |
Collections | |
97.41% |
188 / 193 |
|
94.29% |
33 / 35 |
97 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
create | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
wrap | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getIterator | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
from | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
range | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
repeat | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
empty | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toArray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toList | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
toDictionary | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
5 | |||
where | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
select | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
selectMany | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
concat | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
prepend | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
append | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
any | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
all | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
count | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
5 | |||
first | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
5 | |||
firstOr | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
5 | |||
last | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
6.02 | |||
lastOr | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
6 | |||
single | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
8 | |||
singleOr | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
8 | |||
skip | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
skipWhile | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
4 | |||
take | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
takeWhile | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
reverse | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
aggregate | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
max | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
min | |
60.00% |
6 / 10 |
|
0.00% |
0 / 1 |
8.30 | |||
zip | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\Collection; |
6 | |
7 | use AppendIterator; |
8 | use ArrayIterator; |
9 | use CallbackFilterIterator; |
10 | use Countable; |
11 | use EmptyIterator; |
12 | use Iterator; |
13 | use IteratorAggregate; |
14 | use LimitIterator; |
15 | use Traversable; |
16 | use PeServer\Core\Collection\Arr; |
17 | use PeServer\Core\Throws\ArgumentException; |
18 | use PeServer\Core\Throws\InvalidOperationException; |
19 | use 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 | */ |
31 | class 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 | } |