Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 0 additions & 78 deletions .github/workflows/claude-code-review.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ jobs:
key: "phpstan-result-cache-${{ github.run_id }}"

- name: Run PHPUnit tests
run: vendor/bin/phpunit --configuration=phpunit.xml.dist --testdox --colors=always
run: php -d zend.assertions=1 vendor/bin/phpunit --configuration=phpunit.xml.dist --testdox --colors=always
36 changes: 33 additions & 3 deletions src/Collection/ArrayList.php
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ final public function firstOrFail(?Closure $closure = null)
#[Override]
final public function sole(?Closure $closure = null)
{
$items = $closure === null ? new static($this->elements) : $this->filter($closure);
$items = $closure === null ? new static($this->elements) : $this->filterStrict($closure);
$count = $items->count();

if ($count === 0) {
Expand Down Expand Up @@ -337,17 +337,47 @@ final public function mapStrict(Closure $closure): static
return new static(array_map($closure, $this->elements, $keys));
}

/**
* @template TFlatMapValue
* @param Closure(TValue,int): iterable<TFlatMapValue> $closure
* @return self<TFlatMapValue>
*/
#[Override]
final public function flatMap(Closure $closure): self
{
$result = [];

foreach ($this->elements as $index => $item) {
$mapped = $closure($item, $index);

foreach ($mapped as $subItem) {
$result[] = $subItem;
}
}

return new self($result);
}

/**
* @param Closure(TValue,int): bool $closure
* @return self<TValue>
*/
#[Override]
final public function filter(Closure $closure): static
final public function filter(Closure $closure): self
{
return new self(array_filter($this->elements, $closure, ARRAY_FILTER_USE_BOTH));
}

#[Override]
final public function filterStrict(Closure $closure): static
{
return new static(array_filter($this->elements, $closure, ARRAY_FILTER_USE_BOTH));
}

#[Override]
final public function reject(Closure $closure): static
{
return $this->filter(static fn ($value, $key) => !$closure($value, $key));
return $this->filterStrict(static fn ($value, $key) => !$closure($value, $key));
}

#[Override]
Expand Down
18 changes: 17 additions & 1 deletion src/Collection/List/IArrayList.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ public function merge(self $other): self;
*/
public function map(Closure $closure): self;

/**
* 各要素に関数を適用し、結果を平坦化したコレクションを返す
* @template TFlatMapValue
* @param Closure(TValue,int): iterable<TFlatMapValue> $closure
* @return self<TFlatMapValue>
*/
public function flatMap(Closure $closure): self;

/**
* @param Closure(TValue,int): TValue $closure
* @return static<TValue>
Expand All @@ -134,9 +142,17 @@ public function mapStrict(Closure $closure): static;
/**
* 与えられた真理判定に合格するすべての要素のコレクションを作成する。
* @param Closure(TValue,int): bool $closure
* @return self<TValue>
*/
public function filter(Closure $closure): self;

/**
* 与えられた真理判定に合格するすべての要素のコレクションを作成する。
* (strict version - 正確な型を保持)
* @param Closure(TValue,int): bool $closure
* @return static<TValue>
*/
public function filter(Closure $closure): static;
public function filterStrict(Closure $closure): static;

/**
* 与えられた真理判定に合格しないすべての要素のコレクションを作成する。
Expand Down
4 changes: 2 additions & 2 deletions src/ValueObjectList.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function has(IValueObject $element): bool
*/
public function remove(IValueObject $element): static
{
return $this->filter(static fn (IValueObject $e) => !$e->equals($element));
return $this->filterStrict(static fn (IValueObject $e) => !$e->equals($element));
}

/**
Expand All @@ -45,6 +45,6 @@ public function put(IValueObject $element): static
*/
public function diff(self $other): static
{
return $this->filter(static fn (IValueObject $e) => !$other->has($e));
return $this->filterStrict(static fn (IValueObject $e) => !$other->has($e));
}
}
72 changes: 72 additions & 0 deletions tests/Unit/Collection/ArrayListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -467,4 +467,76 @@ public function sort関数で要素をソートしたコレクションが取得
// 元のコレクションは変更されない(イミュータブル)
$this->assertEquals([3, 1, 4, 2, 5], $collection->toArray());
}

#[Test]
public function filter関数でselfを返すことができる(): void
{
$collection = ArrayList::from([1, 2, 3, 4, 5]);

$filtered = $collection->filter(static fn ($value) => $value % 2 === 0);

// 戻り値がArrayListインスタンス(self)であることを確認
$this->assertInstanceOf(ArrayList::class, $filtered);
$this->assertEquals([1 => 2, 3 => 4], $filtered->toArray());

// 元のコレクションは変更されない(イミュータブル)
$this->assertEquals([1, 2, 3, 4, 5], $collection->toArray());
}

#[Test]
public function filterStrict関数でstaticを返すことができる(): void
{
$collection = ArrayList::from([1, 2, 3, 4, 5]);

$filtered = $collection->filterStrict(static fn ($value) => $value % 2 === 0);

// 戻り値が正確な型(static)であることを確認
$this->assertInstanceOf(ArrayList::class, $filtered);
$this->assertEquals([1 => 2, 3 => 4], $filtered->toArray());

// 元のコレクションは変更されない(イミュータブル)
$this->assertEquals([1, 2, 3, 4, 5], $collection->toArray());
}

#[Test]
public function flatMap関数で各要素を変換して平坦化できる(): void
{
// 基本的な変換(各数値を2倍にして配列に包む)
$collection = ArrayList::from([1, 2, 3]);
$mapped = $collection->flatMap(static fn ($value) => [$value * 2]);

$this->assertInstanceOf(ArrayList::class, $mapped);
$this->assertEquals([2, 4, 6], $mapped->toArray());

// 各要素を複数の要素に展開
$collection2 = ArrayList::from([1, 2, 3]);
$expanded = $collection2->flatMap(static fn ($value) => [$value, $value * 10]);

$this->assertEquals([1, 10, 2, 20, 3, 30], $expanded->toArray());

// 空の配列を返す場合
$collection3 = ArrayList::from([1, 2, 3]);
$filtered = $collection3->flatMap(static fn ($value) => $value % 2 === 0 ? [$value] : []);

$this->assertEquals([2], $filtered->toArray());

// 2次元配列の平坦化(従来のflattenと同等の動作)
$collection4 = ArrayList::from([[1, 2], [3, 4], [5, 6]]);
$flattened = $collection4->flatMap(static fn ($array) => $array);

$this->assertEquals([1, 2, 3, 4, 5, 6], $flattened->toArray());
// 元のコレクションは変更されない(イミュータブル)
$this->assertEquals([1, 2, 3], $collection->toArray());

// objectの2次元配列の平坦化(従来のflattenと同等の動作)
$collection5 = ArrayList::from([
ArrayList::from([1, 2]),
ArrayList::from([3, 4]),
ArrayList::from([5, 6]),
]);
$flattenedObjects = $collection5->flatMap(static fn ($array) => $array);
$this->assertEquals([1, 2, 3, 4, 5, 6], $flattenedObjects->toArray());
// 元のコレクションは変更されない(イミュータブル)
$this->assertEquals([ArrayList::from([1, 2]), ArrayList::from([3, 4]), ArrayList::from([5, 6])], $collection5->toArray());
}
}
Loading