Skip to content

Commit c494feb

Browse files
authored
Merge pull request #53 from xp-forge/feature/zip
Implement Sequence::zip() intermediate operation
2 parents c691c44 + d7c1602 commit c494feb

File tree

4 files changed

+77
-2
lines changed

4 files changed

+77
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ The following operations return a new `Sequence` instance on which more intermed
9999
* **collecting** - Collects elements in this sequence to a `util.data.ICollector` instance. Unlike the terminal operation below, passes the elements on.
100100
* **flatten** - Flattens sequences inside the sequence and returns a new list containing all values from all sequences.
101101
* **distinct** - Returns a new sequence which only consists of unique elements. Uniqueness is calculated using the `util.Objects::hashOf()` method by default (*but can be passed another function*).
102+
* **zip** - Combines values from this sequence with a given enumerable value, optionally using a given transformation function.
102103
* **sorted** - Returns a sorted collection. Can be invoked with a comparator function, a `util.Comparator` instance or the sort flags from PHP's sort() function (e.g. `SORT_NUMERIC | SORT_DESC`).
103104

104105
Terminal operations

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ abstract class Enumeration {
2323
/**
2424
* Verifies a given argument is an enumeration
2525
*
26-
* @param var $arg
26+
* @param ?iterable|util.XPIterator|function(): iterable $arg
2727
* @return iterable
2828
* @throws lang.IllegalArgumentException
2929
*/

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

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function getIterator(): Traversable { yield from $this->elements; }
4444
* Creates a new stream with an enumeration of elements
4545
*
4646
* @see xp://util.data.Enumeration
47-
* @param var... $enumerables an iterator, iterable, generator or array
47+
* @param ?iterable|util.XPIterator|function(): iterable... $enumerables
4848
* @return self
4949
* @throws lang.IllegalArgumentException if type of elements argument is incorrect
5050
*/
@@ -405,6 +405,43 @@ public function map($function) {
405405
return new self($f());
406406
}
407407

408+
/**
409+
* Combines values from this sequence with a given enumerable value,
410+
* optionally using a given transformation function.
411+
*
412+
* @param ?iterable|util.XPIterator|function(): iterable $enumerable
413+
* @param ?function(var, var): var $transform
414+
* @return self
415+
* @throws lang.IllegalArgumentException
416+
*/
417+
public function zip($enumerable, $transform= null) {
418+
$it= self::of($enumerable)->getIterator();
419+
if (null === $transform) {
420+
$f= function() use($it) {
421+
foreach ($this->elements as $key => $element) {
422+
yield $key => [$element, $it->current()];
423+
$it->next();
424+
if (!$it->valid()) break;
425+
}
426+
};
427+
} else {
428+
$op= Functions::$BINARYOP->newInstance($transform);
429+
$f= function() use($it, $op) {
430+
foreach ($this->elements as $key => $element) {
431+
$r= $op($element, $it->current());
432+
if ($r instanceof \Generator) {
433+
yield from $r;
434+
} else {
435+
yield $key => $r;
436+
}
437+
$it->next();
438+
if (!$it->valid()) break;
439+
}
440+
};
441+
}
442+
return new self($f());
443+
}
444+
408445
/**
409446
* Returns a new stream which flattens, mapping the given function to each
410447
* element.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php namespace util\data\unittest;
2+
3+
use unittest\{Assert, Test};
4+
use util\data\Sequence;
5+
6+
class SequenceZipTest extends AbstractSequenceTest {
7+
8+
#[Test]
9+
public function basic_functionality() {
10+
$result= Sequence::of([1, 2, 3, 4])->zip(['a', 'b', 'c', 'd'])->toArray();
11+
Assert::equals([[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd']], $result);
12+
}
13+
14+
#[Test]
15+
public function transform_using_function() {
16+
$result= Sequence::of([1, 2, 3, 4])->zip([1, 0, -1, 6], function($a, $b) { return $a - $b; })->toArray();
17+
Assert::equals([0, 2, 4, -2], $result);
18+
}
19+
20+
#[Test]
21+
public function create_map_with_generator() {
22+
$result= Sequence::of([1, 2, 3, 4])->zip(['a', 'b', 'c', 'd'], function($a, $b) { yield $a => $b; })->toMap();
23+
Assert::equals([1 => 'a', 2 => 'b', 3 => 'c', 4 => 'd'], $result);
24+
}
25+
26+
#[Test]
27+
public function shorter_sequence() {
28+
$result= Sequence::of([1, 2, 3])->zip(['a', 'b', 'c', 'd'])->toArray();
29+
Assert::equals([[1, 'a'], [2, 'b'], [3, 'c']], $result);
30+
}
31+
32+
#[Test]
33+
public function shorter_argument() {
34+
$result= Sequence::of([1, 2, 3, 4])->zip(['a', 'b', 'c'])->toArray();
35+
Assert::equals([[1, 'a'], [2, 'b'], [3, 'c']], $result);
36+
}
37+
}

0 commit comments

Comments
 (0)