Skip to content

Commit 573876d

Browse files
authored
Merge pull request #54 from wiz-develop:endou-mame/issue45
flatMap が想定の動きをしない
2 parents 6eb4027 + a60509e commit 573876d

File tree

3 files changed

+110
-5
lines changed

3 files changed

+110
-5
lines changed

src/Collection/ArrayList.php

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use WizDevelop\PhpValueObject\Collection\Base\CollectionBase;
1616
use WizDevelop\PhpValueObject\Collection\Base\CollectionDefault;
1717
use WizDevelop\PhpValueObject\Collection\Base\CountableDefault;
18+
use WizDevelop\PhpValueObject\Collection\Base\ICollection;
1819
use WizDevelop\PhpValueObject\Collection\Exception\CollectionNotFoundException;
1920
use WizDevelop\PhpValueObject\Collection\Exception\MultipleCollectionsFoundException;
2021
use WizDevelop\PhpValueObject\Collection\List\IArrayList;
@@ -344,18 +345,72 @@ final public function mapStrict(Closure $closure): static
344345
*/
345346
#[Override]
346347
final public function flatMap(Closure $closure): self
348+
{
349+
return $this->map($closure)->collapse(); // @phpstan-ignore-line
350+
}
351+
352+
/**
353+
* @return self<mixed>
354+
*/
355+
#[Override]
356+
final public function flatten(int $depth = PHP_INT_MAX): self
357+
{
358+
$elements = $this->elements;
359+
$flattened = self::flattenInner($elements, $depth);
360+
361+
return new self($flattened); // @phpstan-ignore-line
362+
}
363+
364+
/**
365+
* 多次元配列を単一レベルに平坦化する。
366+
*
367+
* @param iterable<mixed> $array
368+
* @return array<mixed>
369+
*/
370+
private static function flattenInner(iterable $array, int $depth = PHP_INT_MAX): array
347371
{
348372
$result = [];
349373

350-
foreach ($this->elements as $index => $item) {
351-
$mapped = $closure($item, $index);
374+
foreach ($array as $item) {
375+
$item = $item instanceof ICollection ? $item->toArray() : $item;
352376

353-
foreach ($mapped as $subItem) {
354-
$result[] = $subItem;
377+
if (! is_array($item)) {
378+
$result[] = $item;
379+
} else {
380+
$values = $depth === 1
381+
? array_values($item)
382+
: self::flattenInner($item, $depth - 1);
383+
384+
foreach ($values as $value) {
385+
$result[] = $value;
386+
}
387+
}
388+
}
389+
390+
return $result;
391+
}
392+
393+
/**
394+
* 配列の配列を 1 つの配列に折りたたむ。
395+
*
396+
* @return self<mixed>
397+
*/
398+
private function collapse(): self
399+
{
400+
$elements = $this->elements;
401+
$results = [];
402+
403+
foreach ($elements as $values) {
404+
if ($values instanceof ICollection) {
405+
$values = $values->toArray();
406+
} elseif (! is_array($values)) {
407+
continue;
355408
}
409+
410+
$results[] = $values;
356411
}
357412

358-
return new self($result);
413+
return new self(array_values(array_merge([], ...$results)));
359414
}
360415

361416
/**

src/Collection/List/IArrayList.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ public function map(Closure $closure): self;
133133
*/
134134
public function flatMap(Closure $closure): self;
135135

136+
/**
137+
* 多次元配列を単一レベルに平坦化する。
138+
*
139+
* @return self<mixed>
140+
*/
141+
public function flatten(int $depth = PHP_INT_MAX): self;
142+
136143
/**
137144
* @param Closure(TValue,int): TValue $closure
138145
* @return static<TValue>

tests/Unit/Collection/ArrayListTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,49 @@ public function flatMap関数で各要素を変換して平坦化できる(): vo
540540
$this->assertEquals([ArrayList::from([1, 2]), ArrayList::from([3, 4]), ArrayList::from([5, 6])], $collection5->toArray());
541541
}
542542

543+
#[Test]
544+
public function flatMap関数で空配列を含む場合も正しく平坦化できる(): void
545+
{
546+
$collection = ArrayList::from([1, 2, 3]);
547+
548+
// 空配列を返す場合
549+
$flatMapped = $collection->flatMap(static fn ($value) => $value % 2 === 0 ? [] : [$value * 2]);
550+
551+
$this->assertInstanceOf(ArrayList::class, $flatMapped);
552+
$this->assertEquals([2, 6], $flatMapped->toArray());
553+
554+
// 元のコレクションは変更されない(イミュータブル)
555+
$this->assertEquals([1, 2, 3], $collection->toArray());
556+
}
557+
558+
#[Test]
559+
public function flatten関数でネストされたコレクションを平坦化できる(): void
560+
{
561+
$collection = ArrayList::from([[1, 2], [3, 4, [5, 6, [7]]]]);
562+
563+
$flattened = $collection->flatten();
564+
565+
$this->assertInstanceOf(ArrayList::class, $flattened);
566+
$this->assertEquals([1, 2, 3, 4, 5, 6, 7], $flattened->toArray());
567+
568+
// 元のコレクションは変更されない(イミュータブル)
569+
$this->assertEquals([[1, 2], [3, 4, [5, 6, [7]]]], $collection->toArray());
570+
}
571+
572+
#[Test]
573+
public function flatten関数で空のコレクションも正しく平坦化できる(): void
574+
{
575+
$collection = ArrayList::from([[], [1, 2], [], [3, 4]]);
576+
577+
$flattened = $collection->flatten();
578+
579+
$this->assertInstanceOf(ArrayList::class, $flattened);
580+
$this->assertEquals([1, 2, 3, 4], $flattened->toArray());
581+
582+
// 元のコレクションは変更されない(イミュータブル)
583+
$this->assertEquals([[], [1, 2], [], [3, 4]], $collection->toArray());
584+
}
585+
543586
#[Test]
544587
public function filterAs関数で特定のクラスのインスタンスのみを含むコレクションが取得できる(): void
545588
{

0 commit comments

Comments
 (0)