@@ -4804,16 +4804,18 @@ Params:
48044804 pred = Predicate for determining equivalence between range elements.
48054805 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
48064806 elements to filter.
4807+ pp = The $(LREF PickingPolicy), which by default is the first unique
4808+ element from left to right
48074809
48084810Returns:
48094811 An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
48104812 consecutively unique elements in the original range. If $(D r) is also a
48114813 forward range or bidirectional range, the returned range will be likewise.
48124814*/
4813- auto uniq (alias pred = " a == b" , Range )(Range r)
4815+ auto uniq (alias pred = " a == b" , Range )(Range r, PickingPolicy pp = PickingPolicy.first )
48144816if (isInputRange! Range && is (typeof (binaryFun! pred(r.front, r.front)) == bool ))
48154817{
4816- return UniqResult! (binaryFun! pred, Range )(r);
4818+ return UniqResult! (binaryFun! pred, Range )(r, pp );
48174819}
48184820
48194821// /
@@ -4835,13 +4837,64 @@ if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool))
48354837 assert (equal(uniq([ 1 , 1 , 2 , 1 , 1 , 3 , 1 ]), [1 , 2 , 1 , 3 , 1 ]));
48364838}
48374839
4840+ @safe unittest
4841+ {
4842+ import std.algorithm.comparison : equal;
4843+
4844+ struct S
4845+ {
4846+ int i;
4847+ string s;
4848+ }
4849+
4850+ auto arr = [ S(1 , " a" ), S(1 , " b" ), S(2 , " c" ), S(2 , " d" ) ];
4851+
4852+ // Let's consider just the 'i' member for equality
4853+ auto r = arr.uniq! ((a, b) => a.i == b.i);
4854+ assert (r.equal([S(1 , " a" ), S(2 , " c" )]));
4855+ assert (r.front == S(1 , " a" ));
4856+ assert (r.back == S(2 , " c" ));
4857+
4858+ auto r2 = arr.uniq! ((a, b) => a.i == b.i)(PickingPolicy.last);
4859+ assert (r2.equal([S(1 , " b" ), S(2 , " d" )]));
4860+ assert (r2.front == S(1 , " b" ));
4861+ assert (r2.back == S(2 , " d" ));
4862+
4863+ auto arr2 = [ S(1 , " a" ), S(1 , " b" ) ];
4864+ auto r3 = arr2.uniq! ((a, b) => a.i == b.i);
4865+ assert (r3.front == S(1 , " a" ));
4866+ assert (r3.front == r3.back);
4867+ }
4868+
4869+ /**
4870+ Dictates how `uniq` elements should be picked. By default stop at
4871+ the end of the shortest of all ranges.
4872+ */
4873+ enum PickingPolicy
4874+ {
4875+ // / Stop at the first uniq element, from left to right
4876+ first,
4877+ // / Stop at the last uniq element, from left to right
4878+ last
4879+ }
4880+
48384881private struct UniqResult (alias pred, Range )
48394882{
4883+ private PickingPolicy _pickingPolicy;
48404884 Range _input;
4885+ ElementType! Range _front;
4886+ bool _isInFront;
4887+ static if (isBidirectionalRange! Range )
4888+ {
4889+ ElementType! Range _back;
4890+ bool _isInBack;
4891+ bool _isOverlapping;
4892+ }
48414893
4842- this (Range input)
4894+ this (Range input, PickingPolicy pp = PickingPolicy.first )
48434895 {
48444896 _input = input;
4897+ _pickingPolicy = pp;
48454898 }
48464899
48474900 auto opSlice ()
@@ -4852,37 +4905,101 @@ private struct UniqResult(alias pred, Range)
48524905 void popFront ()
48534906 {
48544907 assert (! empty, " Attempting to popFront an empty uniq." );
4855- auto last = _input.front;
4856- do
4908+ if (_input.empty)
48574909 {
4858- _input.popFront();
4910+ _isInFront = false ;
4911+ static if (isBidirectionalRange! Range )
4912+ {
4913+ if (_isOverlapping)
4914+ {
4915+ _isInBack = false ;
4916+ }
4917+ }
4918+ }
4919+ else
4920+ {
4921+ _isInFront = true ;
4922+ _front = _input.front;
4923+ ElementType! Range last;
4924+ do
4925+ {
4926+ last = _input.front;
4927+ _input.popFront();
4928+ }
4929+ while (! _input.empty && pred(last, _input.front));
4930+
4931+ if (_pickingPolicy == PickingPolicy.last)
4932+ {
4933+ _front = last;
4934+ }
48594935 }
4860- while (! _input.empty && pred(last, _input.front));
48614936 }
48624937
48634938 @property ElementType! Range front()
48644939 {
48654940 assert (! empty, " Attempting to fetch the front of an empty uniq." );
4866- return _input.front;
4941+ static if (isBidirectionalRange! Range )
4942+ {
4943+ if (_input.empty && ! _isInFront && _isInBack)
4944+ {
4945+ _isInFront = true ;
4946+ _isOverlapping = true ;
4947+ _front = _back;
4948+ }
4949+ }
4950+ if (! _isInFront)
4951+ {
4952+ popFront();
4953+ }
4954+ return _front;
48674955 }
48684956
48694957 static if (isBidirectionalRange! Range )
48704958 {
48714959 void popBack ()
48724960 {
48734961 assert (! empty, " Attempting to popBack an empty uniq." );
4874- auto last = _input.back;
4875- do
4962+ if (_input.empty)
48764963 {
4877- _input.popBack();
4964+ _isInBack = false ;
4965+ if (_isOverlapping)
4966+ {
4967+ _isInFront = false ;
4968+ }
4969+ }
4970+ else
4971+ {
4972+ _isInBack = true ;
4973+ _back = _input.back;
4974+ ElementType! Range last;
4975+ do
4976+ {
4977+ last = _input.back;
4978+ _input.popBack();
4979+ }
4980+ while (! _input.empty && pred(_back, _input.back));
4981+
4982+ if (_pickingPolicy == PickingPolicy.first)
4983+ {
4984+ _back = last;
4985+ }
48784986 }
4879- while (! _input.empty && pred(last, _input.back));
48804987 }
48814988
48824989 @property ElementType! Range back()
48834990 {
48844991 assert (! empty, " Attempting to fetch the back of an empty uniq." );
4885- return _input.back;
4992+ if (_input.empty && _isInFront && ! _isInBack)
4993+ {
4994+ _isInBack = true ;
4995+ _isOverlapping = true ;
4996+ _back = _front;
4997+ }
4998+ else if (! _isInBack)
4999+ {
5000+ popBack();
5001+ }
5002+ return _back;
48865003 }
48875004 }
48885005
@@ -4892,7 +5009,17 @@ private struct UniqResult(alias pred, Range)
48925009 }
48935010 else
48945011 {
4895- @property bool empty() { return _input.empty; }
5012+ @property bool empty()
5013+ {
5014+ static if (isBidirectionalRange! Range )
5015+ {
5016+ return _input.empty && ! _isInFront && ! _isInBack;
5017+ }
5018+ else
5019+ {
5020+ return _input.empty && ! _isInFront;
5021+ }
5022+ }
48965023 }
48975024
48985025 static if (isForwardRange! Range )
0 commit comments