Skip to content

Commit 2560243

Browse files
authored
Merge pull request #60 from wiz-develop/endou-mame/issue59
ArrayList に mapToDictionary と mapToGroups メソッドを追加する
2 parents b9ea01c + be6cae0 commit 2560243

File tree

3 files changed

+217
-0
lines changed

3 files changed

+217
-0
lines changed

src/Collection/ArrayList.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,52 @@ final public function reduce(Closure $closure, $initial = null)
496496
return $carry;
497497
}
498498

499+
/**
500+
* @template TMapToDictionaryKey of array-key
501+
* @template TMapToDictionaryValue
502+
*
503+
* @param Closure(TValue, int): array<TMapToDictionaryKey, TMapToDictionaryValue> $closure
504+
* @return array<TMapToDictionaryKey, array<int, TMapToDictionaryValue>>
505+
*/
506+
#[Override]
507+
final public function mapToDictionary(Closure $closure): array
508+
{
509+
$dictionary = [];
510+
511+
foreach ($this->elements as $key => $item) {
512+
$pair = $closure($item, $key);
513+
514+
/** @var TMapToDictionaryKey */
515+
$key = key($pair);
516+
517+
/** @var TMapToDictionaryValue */
518+
$value = reset($pair);
519+
520+
if (! isset($dictionary[$key])) {
521+
$dictionary[$key] = [];
522+
}
523+
524+
$dictionary[$key][] = $value;
525+
}
526+
527+
return $dictionary;
528+
}
529+
530+
/**
531+
* @template TMapToGroupsKey of array-key
532+
* @template TMapToGroupsValue
533+
*
534+
* @param Closure(TValue, int): array<TMapToGroupsKey, TMapToGroupsValue> $closure
535+
* @return array<TMapToGroupsKey, static<TMapToGroupsKey>>
536+
*/
537+
#[Override]
538+
final public function mapToGroups(Closure $closure): array
539+
{
540+
$groups = $this->mapToDictionary($closure);
541+
542+
return array_map(static fn ($items) => new static($items), $groups);
543+
}
544+
499545
#[Override]
500546
final public function contains($key): bool
501547
{

src/Collection/List/IArrayList.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,32 @@ public function unique(?Closure $closure = null): static;
198198
*/
199199
public function reduce(Closure $closure, $initial = null);
200200

201+
/**
202+
* Run a dictionary map over the items.
203+
*
204+
* The callback should return an associative array with a single key/value pair.
205+
*
206+
* @template TMapToDictionaryKey of array-key
207+
* @template TMapToDictionaryValue
208+
*
209+
* @param Closure(TValue, int): array<TMapToDictionaryKey, TMapToDictionaryValue> $closure
210+
* @return array<TMapToDictionaryKey, array<int, TMapToDictionaryValue>>
211+
*/
212+
public function mapToDictionary(Closure $closure): array;
213+
214+
/**
215+
* Run a grouping map over the items.
216+
*
217+
* The callback should return an associative array with a single key/value pair.
218+
*
219+
* @template TMapToGroupsKey of array-key
220+
* @template TMapToGroupsValue
221+
*
222+
* @param Closure(TValue, int): array<TMapToGroupsKey, TMapToGroupsValue> $closure
223+
* @return array<TMapToGroupsKey, static<TMapToGroupsKey>>
224+
*/
225+
public function mapToGroups(Closure $closure): array;
226+
201227
/**
202228
* コレクションに指定した要素が含まれているかどうかを判定する
203229
* @param (Closure(TValue,int): bool)|TValue $key

tests/Unit/Collection/ArrayListTest.php

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,4 +620,149 @@ public function values関数でキーが連続した整数にリセットされ
620620
$this->assertEquals($valuesCollection[0], 20);
621621
$this->assertEquals($valuesCollection[1], 30);
622622
}
623+
624+
#[Test]
625+
public function mapToDictionary関数で要素をキーごとにグループ化できる(): void
626+
{
627+
// 基本的なケース:文字列の長さでグループ化
628+
$collection = ArrayList::from(['one', 'two', 'three', 'four', 'five']);
629+
$dictionary = $collection->mapToDictionary(static fn ($value) => [mb_strlen($value) => $value]);
630+
631+
$this->assertArrayHasKey(3, $dictionary);
632+
$this->assertArrayHasKey(4, $dictionary);
633+
$this->assertArrayHasKey(5, $dictionary);
634+
$this->assertEquals(['one', 'two'], $dictionary[3]);
635+
$this->assertEquals(['four', 'five'], $dictionary[4]);
636+
$this->assertEquals(['three'], $dictionary[5]);
637+
638+
// 数値の偶数・奇数でグループ化
639+
$numbers = ArrayList::from([1, 2, 3, 4, 5, 6]);
640+
$evenOddDict = $numbers->mapToDictionary(static fn ($value) => [$value % 2 === 0 ? 'even' : 'odd' => $value]);
641+
642+
$this->assertArrayHasKey('odd', $evenOddDict);
643+
$this->assertArrayHasKey('even', $evenOddDict);
644+
$this->assertEquals([1, 3, 5], $evenOddDict['odd']);
645+
$this->assertEquals([2, 4, 6], $evenOddDict['even']);
646+
647+
// インデックスを使用した場合
648+
$indexed = ArrayList::from(['a', 'b', 'c']);
649+
$indexedDict = $indexed->mapToDictionary(static fn ($value, $index) => [$index % 2 => $value]);
650+
651+
$this->assertEquals(['a', 'c'], $indexedDict[0]);
652+
$this->assertEquals(['b'], $indexedDict[1]);
653+
}
654+
655+
#[Test]
656+
public function mapToDictionary関数で空のコレクションを処理できる(): void
657+
{
658+
$empty = ArrayList::empty();
659+
$dictionary = $empty->mapToDictionary(static fn ($value) => ['key' => $value]);
660+
661+
$this->assertEmpty($dictionary);
662+
}
663+
664+
#[Test]
665+
public function mapToDictionary関数でValueObjectを使用できる(): void
666+
{
667+
$collection = ArrayList::from([
668+
StringValue::from('apple'),
669+
StringValue::from('banana'),
670+
StringValue::from('avocado'),
671+
StringValue::from('blueberry'),
672+
]);
673+
674+
// 最初の文字でグループ化
675+
$dictionary = $collection->mapToDictionary(
676+
static fn (StringValue $value) => [mb_substr($value->value, 0, 1) => $value]
677+
);
678+
679+
$this->assertCount(2, $dictionary);
680+
$this->assertArrayHasKey('a', $dictionary);
681+
$this->assertArrayHasKey('b', $dictionary);
682+
683+
$this->assertCount(2, $dictionary['a']);
684+
$this->assertEquals('apple', $dictionary['a'][0]->value);
685+
$this->assertEquals('avocado', $dictionary['a'][1]->value);
686+
687+
$this->assertCount(2, $dictionary['b']);
688+
$this->assertEquals('banana', $dictionary['b'][0]->value);
689+
$this->assertEquals('blueberry', $dictionary['b'][1]->value);
690+
}
691+
692+
#[Test]
693+
public function mapToGroups関数で要素をキーごとにArrayListのグループにできる(): void
694+
{
695+
// 基本的なケース:文字列の長さでグループ化
696+
$collection = ArrayList::from(['one', 'two', 'three', 'four', 'five']);
697+
$groups = $collection->mapToGroups(static fn ($value) => [mb_strlen($value) => $value]);
698+
699+
$this->assertArrayHasKey(3, $groups);
700+
$this->assertArrayHasKey(4, $groups);
701+
$this->assertArrayHasKey(5, $groups);
702+
703+
// 各グループがArrayListインスタンスであることを確認
704+
$this->assertInstanceOf(ArrayList::class, $groups[3]);
705+
$this->assertInstanceOf(ArrayList::class, $groups[4]);
706+
$this->assertInstanceOf(ArrayList::class, $groups[5]);
707+
708+
// グループの内容を確認
709+
$this->assertEquals(['one', 'two'], $groups[3]->toArray());
710+
$this->assertEquals(['four', 'five'], $groups[4]->toArray());
711+
$this->assertEquals(['three'], $groups[5]->toArray());
712+
713+
// 数値の偶数・奇数でグループ化
714+
$numbers = ArrayList::from([1, 2, 3, 4, 5, 6]);
715+
$evenOddGroups = $numbers->mapToGroups(static fn ($value) => [$value % 2 === 0 ? 'even' : 'odd' => $value]);
716+
717+
$this->assertInstanceOf(ArrayList::class, $evenOddGroups['odd']);
718+
$this->assertInstanceOf(ArrayList::class, $evenOddGroups['even']);
719+
$this->assertEquals([1, 3, 5], $evenOddGroups['odd']->toArray());
720+
$this->assertEquals([2, 4, 6], $evenOddGroups['even']->toArray());
721+
}
722+
723+
#[Test]
724+
public function mapToGroups関数で空のコレクションを処理できる(): void
725+
{
726+
$empty = ArrayList::empty();
727+
$groups = $empty->mapToGroups(static fn ($value) => ['key' => $value]);
728+
729+
$this->assertEmpty($groups);
730+
}
731+
732+
#[Test]
733+
public function mapToGroups関数でインデックスを使用できる(): void
734+
{
735+
$collection = ArrayList::from(['a', 'b', 'c', 'd']);
736+
$groups = $collection->mapToGroups(static fn ($value, $index) => [$index % 2 === 0 ? 'even_index' : 'odd_index' => $value]);
737+
738+
$this->assertInstanceOf(ArrayList::class, $groups['even_index']);
739+
$this->assertInstanceOf(ArrayList::class, $groups['odd_index']);
740+
741+
$this->assertEquals(['a', 'c'], $groups['even_index']->toArray());
742+
$this->assertEquals(['b', 'd'], $groups['odd_index']->toArray());
743+
}
744+
745+
#[Test]
746+
public function mapToGroups関数で作成されたグループはArrayListの全機能を使用できる(): void
747+
{
748+
$collection = ArrayList::from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
749+
$groups = $collection->mapToGroups(static fn ($value) => [$value % 3 => $value]);
750+
751+
// 余り0のグループ
752+
$this->assertInstanceOf(ArrayList::class, $groups[0]);
753+
$remainder0 = $groups[0];
754+
$this->assertEquals([3, 6, 9], $remainder0->toArray());
755+
756+
// グループに対してArrayListのメソッドが使用できる
757+
$doubled = $remainder0->map(static fn ($v) => $v * 2);
758+
$this->assertEquals([6, 12, 18], $doubled->toArray());
759+
760+
$sum = $remainder0->reduce(static fn ($carry, $v) => $carry + $v, 0);
761+
$this->assertEquals(18, $sum);
762+
763+
// 余り1のグループ
764+
$remainder1 = $groups[1];
765+
$filtered = $remainder1->filter(static fn ($v) => $v > 5);
766+
$this->assertEquals([7, 10], $filtered->values()->toArray());
767+
}
623768
}

0 commit comments

Comments
 (0)