diff --git a/Tests/Unit/EnumerableTest.php b/Tests/Unit/EnumerableTest.php index 082858a..1e8382f 100644 --- a/Tests/Unit/EnumerableTest.php +++ b/Tests/Unit/EnumerableTest.php @@ -1268,7 +1268,7 @@ function testAny_fromEnumerable () /** @covers YaLinqo\Enumerable::contains */ - function testAny_contains () + function testContains () { // contains (value) $this->assertEquals( @@ -1282,6 +1282,33 @@ function testAny_contains () E::from(array(1, 2, 3))->contains(4)); } + /** @covers YaLinqo\Enumerable::distinct + */ + function testDistinct () + { + // distinct () + $this->assertEnumEquals( + array(), + E::from(array())->distinct()); + $this->assertEnumEquals( + array(1, 2, 3), + E::from(array(1, 2, 3))->distinct()); + $this->assertEnumEquals( + array(1, 2, 3), + E::from(array(1, 2, 3, 1, 2))->distinct()); + + // distinct (selector) + $this->assertEnumEquals( + array(), + E::from(array())->distinct('$v*$k')); + $this->assertEnumEquals( + array(3 => 1, 2 => 2, 1 => 5), + E::from(array(3 => 1, 2 => 2, 1 => 5))->distinct('$v*$k')); + $this->assertEnumEquals( + array(4 => 1, 1 => 3), + E::from(array(4 => 1, 2 => 2, 1 => 3))->distinct('$v*$k')); + } + #endregion #region Pagination diff --git a/YaLinqo/Enumerable.php b/YaLinqo/Enumerable.php index b3912b5..d682914 100644 --- a/YaLinqo/Enumerable.php +++ b/YaLinqo/Enumerable.php @@ -4,7 +4,7 @@ use YaLinqo, YaLinqo\collections as c, YaLinqo\exceptions as e; // TODO: string syntax: select("new { ... }") -// TODO: linq.js must: Distinct[By], Except[By], Intersect, Union, Cast +// TODO: linq.js must: Except[By], Intersect, Union, Cast // TODO: linq.js must: Zip, Concat, Insert, Let, Memoize, MemoizeAll, BufferWithCount, SequenceEqual, Reverse // TODO: linq.js high: CascadeBreadthFirst, CascadeDepthFirst, Flatten, Scan, PreScan, Alternate, DefaultIfEmpty, Shuffle // TODO: linq.js maybe: Pairwise, PartitionBy, TakeExceptLast, TakeFromLast, Share @@ -800,7 +800,7 @@ public function count ($predicate = null) /** *

Syntax: max () *

Returns the maximum value in a sequence of values. - *

Syntax: max ([selector {{(v, k) ==> value}]) + *

Syntax: max (selector {{(v, k) ==> value}) *

Invokes a transform function on each element of a sequence and returns the maximum value. * @param callback|null $selector {(v, k) ==> value} A transform function to apply to each element. Default: value. * @throws \UnexpectedValueException If sequence contains no elements. @@ -815,9 +815,9 @@ public function max ($selector = null) } /** - *

Syntax: maxBy () + *

Syntax: maxBy (comparer {{(a, b) ==> diff}) *

Returns the maximum value in a sequence of values, using specified comparer. - *

Syntax: maxBy ([selector {{(v, k) ==> value}]) + *

Syntax: maxBy (comparer {{(a, b) ==> diff}, selector {{(v, k) ==> value}) *

Invokes a transform function on each element of a sequence and returns the maximum value, using specified comparer. * @param callback $comparer {(a, b) ==> diff} Difference between a and b: <0 if a<b; 0 if a==b; >0 if a>b * @param callback|null $selector {(v, k) ==> value} A transform function to apply to each element. Default: value. @@ -838,7 +838,7 @@ public function maxBy ($comparer, $selector = null) /** *

Syntax: min () *

Returns the minimum value in a sequence of values. - *

Syntax: min ([selector {{(v, k) ==> value}]) + *

Syntax: min (selector {{(v, k) ==> value}) *

Invokes a transform function on each element of a sequence and returns the minimum value. * @param callback|null $selector {(v, k) ==> value} A transform function to apply to each element. Default: value. * @throws \UnexpectedValueException If sequence contains no elements. @@ -853,9 +853,9 @@ public function min ($selector = null) } /** - *

Syntax: minBy () + *

Syntax: minBy (comparer {{(a, b) ==> diff}) *

Returns the minimum value in a sequence of values, using specified comparer. - *

Syntax: minBy ([selector {{(v, k) ==> value}]) + *

Syntax: minBy (comparer {{(a, b) ==> diff}, selector {{(v, k) ==> value}) *

Invokes a transform function on each element of a sequence and returns the minimum value, using specified comparer. * @param callback $comparer {(a, b) ==> diff} Difference between a and b: <0 if a<b; 0 if a==b; >0 if a>b * @param callback|null $selector {(v, k) ==> value} A transform function to apply to each element. Default: value. @@ -954,6 +954,28 @@ public function contains ($value) return false; } + /** + *

Syntax: distinct () + *

Returns distinct elements from a sequence. + *

Syntax: distinct (selector {{(v, k) ==> value}) + *

Invokes a transform function on each element of a sequence and returns distinct elements. + * @param callback|null $selector {(v, k) ==> value} A transform function to apply to each element. Default: value. + * @return Enumerable A sequence that contains distinct elements of the input sequence. + */ + public function distinct ($selector = null) + { + $selector = Utils::createLambda($selector, 'v,k', Functions::$value); + + $dic = new c\Dictionary(); + return $this->where(function ($v, $k) use ($dic, $selector) { + $key = call_user_func($selector, $v, $k); + if ($dic->offsetExists($key)) + return false; + $dic->offsetSet($key, true); + return true; + }); + } + #endregion #region Pagination