Skip to content

Commit a18d46f

Browse files
author
Bl00D4NGEL
committed
feat: add every, none and some functions to collection
1 parent 071ae7e commit a18d46f

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

src/Domain/Collection.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ final public function __construct(
3636
* @param iterable<T> $items
3737
* @param class-string<T>|null $itemType
3838
* @return self<T>
39+
* @throws Assert\AssertionFailedException
3940
*/
4041
public static function fromIterable(iterable $items, ?string $itemType = null): static
4142
{
@@ -50,6 +51,75 @@ public static function fromIterable(iterable $items, ?string $itemType = null):
5051
return new static(iterator_to_array($items), $itemType);
5152
}
5253

54+
/**
55+
* Returns true if every value in the collection passes the callback truthy test. Opposite of self::none().
56+
* Callback arguments will be element, index, collection.
57+
* Function short-circuits on first falsy return value
58+
*
59+
* @param ?callable(T, int, static): bool $callback
60+
* @return bool
61+
*/
62+
public function every(callable $callback = null): bool
63+
{
64+
if ($callback === null) {
65+
$callback = static fn ($item, $index, $self) => $item;
66+
}
67+
68+
foreach ($this->items as $index => $item) {
69+
if (!$callback($item, $index, $this)) {
70+
return false;
71+
}
72+
}
73+
74+
return true;
75+
}
76+
77+
/**
78+
* Returns true if every value in the collection passes the callback falsy test. Opposite of self::every().
79+
* Callback arguments will be element, index, collection.
80+
* Function short-circuits on first truthy return value
81+
*
82+
* @param ?callable(T, int, static): bool $callback
83+
* @return bool
84+
*/
85+
public function none(callable $callback = null): bool
86+
{
87+
if ($callback === null) {
88+
$callback = static fn ($item, $index, $self) => $item;
89+
}
90+
91+
foreach ($this->items as $index => $item) {
92+
if ($callback($item, $index, $this)) {
93+
return false;
94+
}
95+
}
96+
97+
return true;
98+
}
99+
100+
/**
101+
* Returns true if at least one value in the collection passes the callback truthy test.
102+
* Callback arguments will be element, index, collection.
103+
* Function short-circuits on first truthy return value
104+
*
105+
* @param ?callable(T, int, static): bool $callback
106+
* @return bool
107+
*/
108+
public function some(callable $callback = null): bool
109+
{
110+
if ($callback === null) {
111+
$callback = static fn ($item, $index, $self) => $item;
112+
}
113+
114+
foreach ($this->items as $index => $item) {
115+
if ($callback($item, $index, $this)) {
116+
return true;
117+
}
118+
}
119+
120+
return false;
121+
}
122+
53123
/**
54124
* Add one or more items to the collection. It **does not** modify the
55125
* current collection, but returns a new one.

tests/Domain/CollectionTest.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,4 +320,115 @@ public function testFromIterable(): void
320320
$collectionFromGenerator = Collection::fromIterable($generatorFn());
321321
$this->assertSame($items, iterator_to_array($collectionFromGenerator));
322322
}
323+
324+
public function testEvery(): void
325+
{
326+
$items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
327+
$collection = new Collection($items);
328+
329+
$this->assertFalse($collection->every(static fn ($item) => $item > 10));
330+
$this->assertFalse($collection->every(static fn ($item) => $item > 5));
331+
$this->assertTrue($collection->every(static fn ($item) => $item > 0));
332+
}
333+
334+
public function testEveryWithoutArgumentDefaultsToTruthyCheck(): void
335+
{
336+
$this->assertTrue((new Collection([1, true]))->every());
337+
$this->assertTrue((new Collection([1, true]))->every());
338+
$this->assertFalse((new Collection([null, false]))->every());
339+
$this->assertFalse((new Collection([false, null]))->every());
340+
$this->assertFalse((new Collection([0, false]))->every());
341+
}
342+
343+
public function testEveryReturnsTrueOnEmptyCollection(): void
344+
{
345+
$this->assertTrue((new Collection())->every(static fn ($item) => false));
346+
}
347+
348+
public function testEveryShortCircuitsOnFirstFalsyValue(): void
349+
{
350+
$items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
351+
$collection = new Collection($items);
352+
353+
$collection->every(function ($item, $index, $c) use ($collection): bool {
354+
// First item already returns false therefore the index should never be something other than 0
355+
$this->assertSame(0, $index);
356+
$this->assertSame($c, $collection);
357+
return false;
358+
});
359+
}
360+
361+
public function testNone(): void
362+
{
363+
$items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
364+
$collection = new Collection($items);
365+
366+
$this->assertTrue($collection->none(static fn ($item) => $item > 10));
367+
$this->assertFalse($collection->none(static fn ($item) => $item > 5));
368+
$this->assertFalse($collection->none(static fn ($item) => $item > 0));
369+
}
370+
371+
public function testNoneWithoutArgumentDefaultsToTruthyCheck(): void
372+
{
373+
$this->assertFalse((new Collection([1, true]))->none());
374+
$this->assertFalse((new Collection([1, true]))->none());
375+
$this->assertTrue((new Collection([null, false]))->none());
376+
$this->assertTrue((new Collection([false, null]))->none());
377+
$this->assertTrue((new Collection([0, false]))->none());
378+
}
379+
380+
public function testNoneReturnsFalseOnEmptyCollection(): void
381+
{
382+
$this->assertTrue((new Collection())->none(static fn ($item) => true));
383+
}
384+
385+
public function testNoneShortCircuitsOnFirstFalsyValue(): void
386+
{
387+
$items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
388+
$collection = new Collection($items);
389+
390+
$collection->none(function ($item, $index, $c) use ($collection): bool {
391+
// First item already returns true therefore the index should never be something other than 0
392+
$this->assertSame(0, $index);
393+
$this->assertSame($c, $collection);
394+
return true;
395+
});
396+
}
397+
398+
public function testSome(): void
399+
{
400+
$items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
401+
$collection = new Collection($items);
402+
403+
$this->assertFalse($collection->some(static fn ($item) => $item > 10));
404+
$this->assertTrue($collection->some(static fn ($item) => $item > 5));
405+
$this->assertTrue($collection->some(static fn ($item) => $item > 0));
406+
}
407+
408+
public function testSomeWithoutArgumentDefaultsToTruthyCheck(): void
409+
{
410+
$this->assertTrue((new Collection([1, true]))->some());
411+
$this->assertTrue((new Collection([1, true]))->some());
412+
$this->assertFalse((new Collection([null, false]))->some());
413+
$this->assertFalse((new Collection([false, null]))->some());
414+
$this->assertFalse((new Collection([0, false]))->some());
415+
}
416+
417+
public function testSomeReturnsFalseOnEmptyCollection(): void
418+
{
419+
$this->assertFalse((new Collection())->some(static fn ($item) => true));
420+
}
421+
422+
public function testSomeShortCircuitsOnFirstFalsyValue(): void
423+
{
424+
$items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
425+
$collection = new Collection($items);
426+
427+
$collection->some(function ($item, $index, $c) use ($collection): bool {
428+
// First item already returns true therefore the index should never be something other than 0
429+
$this->assertSame(0, $index);
430+
$this->assertSame($c, $collection);
431+
return true;
432+
});
433+
}
323434
}

0 commit comments

Comments
 (0)