Skip to content

Commit 4c5b587

Browse files
authored
Merge pull request #59 from xp-forge/feature/except
Add `except()`, returns a sequence with values except those passed
2 parents a279583 + cfec996 commit 4c5b587

File tree

3 files changed

+79
-2
lines changed

3 files changed

+79
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ The following operations return a new `Sequence` instance on which more intermed
9494
* **limit** - Stops iteration when limit is reached. Using `limit(10)` stops iteration once ten elements have been returned, `limit(function($e) { return 'stop' === $e; })` will stop once the first element equal to the string *stop* is encountered.
9595
* **filter** - Filters the sequence by a given criterion. The new sequence will only contain values for which it returns true. Accepts a function or a `util.Filter` instance.
9696
* **map** - Maps each element in the sequence by applying a function on it and returning a new sequence with the return value of that function.
97+
* **except** - Returns the sequence with all values except the ones given.
9798
* **peek** - Calls a function for each element in the sequence; especially useful for debugging, e.g. `peek('var_dump', [])`.
9899
* **counting** - Increments the integer given as its argument for each element in the sequence.
99100
* **collecting** - Collects elements in this sequence to a `util.data.ICollector` instance. Unlike the terminal operation below, passes the elements on.

src/main/php/util/data/Sequence.class.php

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@
88
* Sequences API for PHP
99
*
1010
* @test util.data.unittest.SequenceTest
11-
* @test util.data.unittest.SequenceCreationTest
12-
* @test util.data.unittest.SequenceSortingTest
1311
* @test util.data.unittest.SequenceCollectionTest
1412
* @test util.data.unittest.SequenceConcatTest
13+
* @test util.data.unittest.SequenceCreationTest
14+
* @test util.data.unittest.SequenceExceptTest
1515
* @test util.data.unittest.SequenceFilteringTest
1616
* @test util.data.unittest.SequenceFlatteningTest
1717
* @test util.data.unittest.SequenceIteratorTest
1818
* @test util.data.unittest.SequenceMappingTest
1919
* @test util.data.unittest.SequenceReductionTest
2020
* @test util.data.unittest.SequenceResultSetTest
2121
* @test util.data.unittest.SequenceSkipTest
22+
* @test util.data.unittest.SequenceSortingTest
2223
* @test util.data.unittest.SequenceWindowTest
2324
*/
2425
class Sequence implements Value, IteratorAggregate {
@@ -443,6 +444,36 @@ public function map($function) {
443444
return new self($f());
444445
}
445446

447+
/**
448+
* Returns a new sequence yielding values except for those passed to this
449+
* method. Uses `util.Objects::equal()` to compare values.
450+
*
451+
* @param var... $values
452+
* @return self
453+
*/
454+
public function except(... $values) {
455+
if (empty($values)) {
456+
return $this;
457+
} else if (1 === sizeof($values)) {
458+
$value= current($values);
459+
$f= function() use($value) {
460+
foreach ($this->elements as $key => $element) {
461+
Objects::equal($element, $value) || yield $key => $element;
462+
}
463+
};
464+
} else {
465+
$f= function() use($values) {
466+
foreach ($this->elements as $key => $element) {
467+
foreach ($values as $value) {
468+
if (Objects::equal($element, $value)) continue 2;
469+
}
470+
yield $key => $element;
471+
}
472+
};
473+
}
474+
return new self($f());
475+
}
476+
446477
/**
447478
* Combines values from this sequence with a given enumerable value,
448479
* optionally using a given transformation function.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php namespace util\data\unittest;
2+
3+
use test\{Assert, Test};
4+
use util\data\Sequence;
5+
6+
class SequenceExceptTest extends AbstractSequenceTest {
7+
8+
#[Test]
9+
public function except_empty() {
10+
$this->assertSequence([1, 2, 3, 4], Sequence::of([1, 2, 3, 4])->except());
11+
}
12+
13+
#[Test]
14+
public function except_one() {
15+
$this->assertSequence([1, 2, 4], Sequence::of([1, 2, 3, 4])->except(3));
16+
}
17+
18+
#[Test]
19+
public function except_multiple() {
20+
$this->assertSequence([1, 3], Sequence::of([1, 2, 3, 4])->except(2, 4));
21+
}
22+
23+
#[Test]
24+
public function except_varargs() {
25+
$this->assertSequence([1, 3], Sequence::of([1, 2, 3, 4])->except(...[2, 4]));
26+
}
27+
28+
#[Test]
29+
public function except_uses_object_comparison() {
30+
$a= new Person(1, 'A');
31+
$b= new Person(2, 'B');
32+
$c= new Person(3, 'C');
33+
34+
$this->assertSequence([$a, $c], Sequence::of([$a, $b, $c])->except(new Person(2, 'B')));
35+
}
36+
37+
#[Test]
38+
public function except_uses_array_comparison() {
39+
$a= ['id' => 1, 'name' => 'A'];
40+
$b= ['id' => 2, 'name' => 'B'];
41+
$c= ['id' => 3, 'name' => 'C'];
42+
43+
$this->assertSequence([$a, $c], Sequence::of([$a, $b, $c])->except(['id' => 2, 'name' => 'B']));
44+
}
45+
}

0 commit comments

Comments
 (0)