Skip to content

Commit 3a1d57c

Browse files
authored
feat: Add select() as primary method, filter() becomes alias (#285)
1 parent 4257d82 commit 3a1d57c

File tree

4 files changed

+90
-29
lines changed

4 files changed

+90
-29
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
composer remove --no-update --dev \
5353
phpstan/phpstan vimeo/psalm \
5454
infection/infection friendsofphp/php-cs-fixer php-coveralls/php-coveralls
55-
composer update --prefer-dist --no-interaction --no-progress
55+
composer install --prefer-dist --no-interaction --no-progress
5656
5757
- name: Execute tests
5858
run: |
@@ -91,7 +91,7 @@ jobs:
9191
composer remove --no-update --dev \
9292
phpstan/phpstan vimeo/psalm \
9393
friendsofphp/php-cs-fixer
94-
composer update --prefer-dist --no-interaction --no-progress
94+
composer install --prefer-dist --no-interaction --no-progress
9595
9696
- name: Generate coverage
9797
run: |
@@ -160,7 +160,7 @@ jobs:
160160
- name: Install dependencies
161161
run: |
162162
composer remove --no-update --dev infection/infection
163-
composer update --prefer-dist --no-interaction --no-progress
163+
composer install --prefer-dist --no-interaction --no-progress
164164
165165
- name: Validate composer.json
166166
run: |

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ All entry points always return an instance of the pipeline.
132132
| `unpack()` | Unpacks arrays into arguments for a callback. Flattens inputs if no callback provided. | |
133133
| `chunk()` | Chunks the pipeline into arrays of specified length. | `array_chunk` |
134134
| `chunkBy()` | Chunks the pipeline into arrays with variable sizes. Size 0 produces empty arrays. | |
135-
| `filter()` | Removes elements unless a callback returns true. Removes falsey values if no callback provided. | `array_filter`, `Where` |
135+
| `select()` | Selects elements for which the callback returns true. By default only removes `null` and `false`. | `array_filter`, `filter`, `Where` |
136+
| `filter()` | Alias for `select()` with `strict: false` default. Removes all falsy values like `array_filter`. | `array_filter` |
136137
| `tap()` | Performs side effects on each element without changing the values in the pipeline. | |
137138
| `skipWhile()` | Skips elements while the predicate returns true, and keeps everything after the predicate return false just once. | |
138139
| `slice()` | Extracts a slice from the inputs. Keys are not discarded intentionally. Supports negative values for both arguments. | `array_slice` |
@@ -351,21 +352,23 @@ $pipeline->unpack(function ($elementOfA, $elementOfB, $elementOfC) {
351352

352353
With iterators with unequal number of elements, missing elements are left as nulls.
353354

354-
## `$pipeline->filter()`
355+
## `$pipeline->select()`
355356

356-
Takes a filter callback not unlike that of `array_filter`.
357+
Selects elements for which the callback returns true. `filter()` is an alias with `strict: false` default.
357358

358359
```php
359-
$pipeline->filter(function ($item) {
360+
$pipeline->select(function ($item) {
360361
return $item->isGood() && $item->amount > 0;
361362
});
362363
```
363364

364-
The pipeline has a default callback with the same effect as in `array_filter`: it'll remove all falsy values.
365+
By default, `select()` only removes `null` and `false` values.
365366

366-
With the optional `strict` parameter, it only removes strictly `null` or `false`:
367+
With the optional `strict: false` parameter, it removes all falsy values like `array_filter`:
367368
```php
368-
$pipeline->filter(strict: true);
369+
$pipeline->select(strict: false);
370+
// Or use filter() which defaults to non-strict:
371+
$pipeline->filter();
369372
```
370373

371374
## `$pipeline->slice()`

src/Standard.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ private static function applyOnce(iterable $previous, callable $func): Generator
486486
}
487487

488488
/**
489-
* Removes elements unless a callback returns true.
489+
* Selects elements for which the callback returns true.
490490
*
491491
* With no callback drops all null and false values (not unlike array_filter does by default).
492492
*
@@ -496,7 +496,7 @@ private static function applyOnce(iterable $previous, callable $func): Generator
496496
* @phpstan-self-out self<TKey, TValue>
497497
* @return Standard<TKey, TValue>
498498
*/
499-
public function filter(?callable $func = null, bool $strict = false): self
499+
public function select(?callable $func = null, bool $strict = true): self
500500
{
501501
// No-op: an empty array or null.
502502
if ($this->empty()) {
@@ -517,6 +517,24 @@ public function filter(?callable $func = null, bool $strict = false): self
517517
return $this;
518518
}
519519

520+
/**
521+
* Removes elements unless a callback returns true. Alias for select().
522+
*
523+
* With no callback drops all null and false values (not unlike array_filter does by default).
524+
*
525+
* @see select()
526+
*
527+
* @param null|callable(TValue): bool $func A callback that accepts a single value and returns a boolean value.
528+
* @param bool $strict When true, only `null` and `false` are filtered out.
529+
*
530+
* @phpstan-self-out self<TKey, TValue>
531+
* @return Standard<TKey, TValue>
532+
*/
533+
public function filter(?callable $func = null, bool $strict = false): self
534+
{
535+
return $this->select($func, $strict);
536+
}
537+
520538
/**
521539
* Resolves a nullable predicate into a sensible non-null callable.
522540
*/
Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use ArrayIterator;
2424
use PHPUnit\Framework\TestCase;
2525
use Pipeline\Standard;
26+
use PHPUnit\Framework\Attributes\DataProvider;
2627

2728
use function iterator_to_array;
2829
use function Pipeline\fromValues;
@@ -33,7 +34,7 @@
3334
*
3435
* @internal
3536
*/
36-
final class FilterTest extends TestCase
37+
final class SelectTest extends TestCase
3738
{
3839
private const NON_STRICT_FALSE_VALUES = [
3940
0,
@@ -46,20 +47,20 @@ final class FilterTest extends TestCase
4647
public function testStandardStringFunctions(): void
4748
{
4849
$pipeline = new Standard(new ArrayIterator([1, 2, 'foo', 'bar']));
49-
$pipeline->filter('is_int');
50+
$pipeline->select('is_int');
5051

5152
$this->assertSame([1, 2], iterator_to_array($pipeline));
5253
}
5354

5455
public function testStandardFunctions(): void
5556
{
5657
$pipeline = new Standard(new ArrayIterator([1, 2, 'foo', 'bar']));
57-
$pipeline->filter(is_int(...));
58+
$pipeline->select(is_int(...));
5859

5960
$this->assertSame([1, 2], iterator_to_array($pipeline));
6061
}
6162

62-
public function testFilterAnyFalseValueDefaultCallback(): void
63+
public function testSelectAnyFalseValueDefaultCallback(): void
6364
{
6465
$pipeline = map(function () {
6566
yield false;
@@ -71,12 +72,12 @@ public function testFilterAnyFalseValueDefaultCallback(): void
7172
yield null;
7273
});
7374

74-
$pipeline->filter();
75+
$pipeline->select(strict: false);
7576

7677
$this->assertCount(0, $pipeline->toList());
7778
}
7879

79-
public function testFilterAnyFalseValueDefaultCallbackStrict(): void
80+
public function testSelectAnyFalseValueDefaultCallbackStrict(): void
8081
{
8182
$pipeline = map(function () {
8283
yield false;
@@ -88,12 +89,12 @@ public function testFilterAnyFalseValueDefaultCallbackStrict(): void
8889
yield null;
8990
});
9091

91-
$pipeline->filter(strict: true);
92+
$pipeline->select();
9293

9394
$this->assertCount(5, $pipeline->toList());
9495
}
9596

96-
public function testFilterAnyFalseValueCustomCallback(): void
97+
public function testSelectAnyFalseValueCustomCallback(): void
9798
{
9899
$pipeline = map(function () {
99100
yield false;
@@ -106,12 +107,12 @@ public function testFilterAnyFalseValueCustomCallback(): void
106107
yield 1;
107108
});
108109

109-
$pipeline->filter('intval', strict: false);
110+
$pipeline->select('intval', strict: false);
110111

111112
$this->assertSame([1], $pipeline->toList());
112113
}
113114

114-
public function testFilterStrictMode(): void
115+
public function testSelectStrictMode(): void
115116
{
116117
$pipeline = map(function () {
117118
yield false;
@@ -120,12 +121,12 @@ public function testFilterStrictMode(): void
120121
yield from self::NON_STRICT_FALSE_VALUES;
121122
});
122123

123-
$pipeline->filter(strict: true);
124+
$pipeline->select(strict: true);
124125

125126
$this->assertSame(self::NON_STRICT_FALSE_VALUES, $pipeline->toList());
126127
}
127128

128-
public function testFilterStrictModeWithPredicate(): void
129+
public function testSelectStrictModeWithPredicate(): void
129130
{
130131
$pipeline = map(function () {
131132
yield false;
@@ -134,23 +135,62 @@ public function testFilterStrictModeWithPredicate(): void
134135
yield from self::NON_STRICT_FALSE_VALUES;
135136
});
136137

137-
$pipeline->filter(fn($value) => $value, strict: true);
138+
$pipeline->select(fn($value) => $value, strict: true);
138139

139140
$this->assertSame(self::NON_STRICT_FALSE_VALUES, $pipeline->toList());
140141
}
141142

142-
public function testFilterNonStrictMode(): void
143+
public function testSelectNonStrictMode(): void
143144
{
144145
$pipeline = fromValues(false, null, '');
145-
$pipeline->filter(strict: false);
146+
$pipeline->select(strict: false);
146147
$this->assertCount(0, $pipeline);
147148
}
148149

149-
public function testFilterUnprimed(): void
150+
public function testSelectUnprimed(): void
150151
{
151152
$pipeline = new Standard();
152-
$pipeline->filter()->unpack();
153+
$pipeline->select()->unpack();
153154

154155
$this->assertSame([], $pipeline->toList());
155156
}
157+
158+
public function testFilterIsNotStrictByDefault(): void
159+
{
160+
$pipeline = $this->getMockBuilder(Standard::class)
161+
->setConstructorArgs([[1]])
162+
->onlyMethods(['select'])
163+
->getMock();
164+
165+
$pipeline->expects($this->once())
166+
->method('select')
167+
->with(null, false)
168+
->willReturn($pipeline);
169+
170+
$pipeline->filter();
171+
}
172+
173+
public static function provideFilterIsEquivalentToSelect(): iterable
174+
{
175+
yield [null, false];
176+
yield [null, true];
177+
yield [fn($value) => true, false];
178+
yield [fn($value) => true, true];
179+
}
180+
181+
#[DataProvider('provideFilterIsEquivalentToSelect')]
182+
public function testFilterIsEquivalentToSelect(?callable $func, bool $strict): void
183+
{
184+
$pipeline = $this->getMockBuilder(Standard::class)
185+
->setConstructorArgs([[1]])
186+
->onlyMethods(['select'])
187+
->getMock();
188+
189+
$pipeline->expects($this->once())
190+
->method('select')
191+
->with($func, $strict)
192+
->willReturn($pipeline);
193+
194+
$pipeline->filter($func, $strict);
195+
}
156196
}

0 commit comments

Comments
 (0)