Skip to content

Commit cc86448

Browse files
committed
Add support for specifying iteration direction
1 parent 4018be3 commit cc86448

File tree

4 files changed

+303
-6
lines changed

4 files changed

+303
-6
lines changed

lib/node_modules/@stdlib/array/to-circular-iterator/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ The returned iterator protocol-compliant object has the following properties:
8383
The function accepts the following `options`:
8484

8585
- **iter**: number of iterations. Default: `1e308`.
86+
- **dir**: iteration direction. If set to `-1`, the iterator iterates over elements from right-to-left. Default: `1`.
8687

8788
To limit the number of iterations, set the `iter` option.
8889

@@ -112,6 +113,36 @@ var bool = it.next().done;
112113
// returns true
113114
```
114115

116+
To iterate over elements from right to left, set the `dir` option to `-1`.
117+
118+
```javascript
119+
var opts = {
120+
'dir': -1
121+
};
122+
var it = circarray2iterator( [ 1, 2, 3, 4 ], opts );
123+
// returns <Object>
124+
125+
var v = it.next().value;
126+
// returns 4
127+
128+
v = it.next().value;
129+
// returns 3
130+
131+
v = it.next().value;
132+
// returns 2
133+
134+
v = it.next().value;
135+
// returns 1
136+
137+
v = it.next().value;
138+
// returns 4
139+
140+
v = it.next().value;
141+
// returns 3
142+
143+
// ...
144+
```
145+
115146
To invoke a function for each `src` value, provide a callback function.
116147

117148
```javascript

lib/node_modules/@stdlib/array/to-circular-iterator/docs/repl.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
options.iter: integer (optional)
3030
Number of iterations. Default: 1e308.
3131

32+
options.dir: integer (optional)
33+
Iteration direction. If set to `-1`, an iterator iterates over elements
34+
from right-to-left. Default: 1.
35+
3236
mapFcn: Function (optional)
3337
Function to invoke for each iterated value.
3438

lib/node_modules/@stdlib/array/to-circular-iterator/lib/main.js

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ var iteratorSymbol = require( '@stdlib/symbol/iterator' );
3737
* @param {ArrayLikeObject} src - input value
3838
* @param {Options} [options] - function options
3939
* @param {NonNegativeInteger} [options.iter] - number of iterations
40+
* @param {integer} [options.dir=1] - iteration direction
4041
* @param {Function} [mapFcn] - function to invoke for each iterated value
4142
* @param {*} [thisArg] - execution context
4243
* @throws {TypeError} first argument must be an array-like object
@@ -72,7 +73,8 @@ function circarray2iterator( src ) {
7273
throw new TypeError( 'invalid argument. First argument must be an array-like object. Value: `' + src + '`.' );
7374
}
7475
opts = {
75-
'iter': 1e308 // ~infinity
76+
'iter': 1e308, // ~infinity
77+
'dir': 1 // left to right iteration
7678
};
7779
if ( arguments.length > 1 ) {
7880
if ( isObject( arguments[ 1 ] ) ) {
@@ -90,6 +92,12 @@ function circarray2iterator( src ) {
9092
throw new TypeError( 'invalid option. `iter` option must be a nonnegative integer. Option: `' + options.iter + '`.' );
9193
}
9294
}
95+
if ( hasOwnProp( options, 'dir' ) ) {
96+
opts.dir = options.dir;
97+
if ( options.dir !== 1 && options.dir !== -1 ) {
98+
throw new TypeError( 'invalid option. `dir` option must be either `1` or `-1`. Option: `' + options.dir + '`.' );
99+
}
100+
}
93101
} else {
94102
fcn = arguments[ 1 ];
95103
if ( !isFunction( fcn ) ) {
@@ -99,14 +107,23 @@ function circarray2iterator( src ) {
99107
}
100108
}
101109
count = 0;
102-
i = -1;
103110

104111
// Create an iterator protocol-compliant object:
105112
iter = {};
106113
if ( fcn ) {
107-
setReadOnly( iter, 'next', next1 );
114+
if ( opts.dir === 1 ) {
115+
i = -1;
116+
setReadOnly( iter, 'next', next1a );
117+
} else {
118+
i = src.length;
119+
setReadOnly( iter, 'next', next1b );
120+
}
121+
} else if ( opts.dir === 1 ) {
122+
i = -1;
123+
setReadOnly( iter, 'next', next2a );
108124
} else {
109-
setReadOnly( iter, 'next', next2 );
125+
i = src.length;
126+
setReadOnly( iter, 'next', next2b );
110127
}
111128
setReadOnly( iter, 'return', end );
112129

@@ -122,7 +139,7 @@ function circarray2iterator( src ) {
122139
* @private
123140
* @returns {Object} iterator protocol-compliant object
124141
*/
125-
function next1() {
142+
function next1a() {
126143
i = (i+1) % src.length;
127144
count += 1;
128145
if ( FLG || count > opts.iter || src.length === 0 ) {
@@ -142,7 +159,30 @@ function circarray2iterator( src ) {
142159
* @private
143160
* @returns {Object} iterator protocol-compliant object
144161
*/
145-
function next2() {
162+
function next1b() {
163+
i -= 1;
164+
if ( i < 0 ) {
165+
i += src.length;
166+
}
167+
count += 1;
168+
if ( FLG || count > opts.iter || src.length === 0 ) {
169+
return {
170+
'done': true
171+
};
172+
}
173+
return {
174+
'value': fcn.call( thisArg, src[ i ], i, count, src ),
175+
'done': false
176+
};
177+
}
178+
179+
/**
180+
* Returns an iterator protocol-compliant object containing the next iterated value.
181+
*
182+
* @private
183+
* @returns {Object} iterator protocol-compliant object
184+
*/
185+
function next2a() {
146186
i = (i+1) % src.length;
147187
count += 1;
148188
if ( FLG || count > opts.iter || src.length === 0 ) {
@@ -156,6 +196,29 @@ function circarray2iterator( src ) {
156196
};
157197
}
158198

199+
/**
200+
* Returns an iterator protocol-compliant object containing the next iterated value.
201+
*
202+
* @private
203+
* @returns {Object} iterator protocol-compliant object
204+
*/
205+
function next2b() {
206+
i -= 1;
207+
if ( i < 0 ) {
208+
i += src.length;
209+
}
210+
count += 1;
211+
if ( FLG || count > opts.iter || src.length === 0 ) {
212+
return {
213+
'done': true
214+
};
215+
}
216+
return {
217+
'value': src[ i ],
218+
'done': false
219+
};
220+
}
221+
159222
/**
160223
* Finishes an iterator.
161224
*

lib/node_modules/@stdlib/array/to-circular-iterator/test/test.js

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,73 @@ tape( 'the function throws an error if provided an `iter` option which is not a
239239
}
240240
});
241241

242+
tape( 'the function throws an error if provided a `dir` option which is neither `1` nor `-1` (no callback)', function test( t ) {
243+
var values;
244+
var i;
245+
246+
values = [
247+
'5',
248+
5,
249+
0,
250+
-5,
251+
NaN,
252+
true,
253+
false,
254+
null,
255+
void 0,
256+
[],
257+
{},
258+
function noop() {}
259+
];
260+
261+
for ( i = 0; i < values.length; i++ ) {
262+
t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
263+
}
264+
t.end();
265+
266+
function badValue( value ) {
267+
return function badValue() {
268+
circarray2iterator( [ 1, 2, 3, 4 ], {
269+
'dir': value
270+
});
271+
};
272+
}
273+
});
274+
275+
tape( 'the function throws an error if provided a `dir` option which is neither `1` nor `-1` (callback)', function test( t ) {
276+
var values;
277+
var i;
278+
279+
values = [
280+
'5',
281+
5,
282+
0,
283+
-5,
284+
NaN,
285+
true,
286+
false,
287+
null,
288+
void 0,
289+
[],
290+
{},
291+
function noop() {}
292+
];
293+
294+
for ( i = 0; i < values.length; i++ ) {
295+
t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
296+
}
297+
t.end();
298+
299+
function badValue( value ) {
300+
return function badValue() {
301+
var opts = {
302+
'dir': value
303+
};
304+
circarray2iterator( [ 1, 2, 3, 4 ], opts, noop );
305+
};
306+
}
307+
});
308+
242309
tape( 'the function returns an iterator protocol-compliant object which repeatedly iterates over elements of an array-like object (array)', function test( t ) {
243310
var expected;
244311
var actual;
@@ -667,6 +734,138 @@ tape( 'the function supports limiting the number of iterations (callback)', func
667734
}
668735
});
669736

737+
tape( 'the function supports specifying the iteration direction', function test( t ) {
738+
var expected;
739+
var actual;
740+
var values;
741+
var opts;
742+
var it;
743+
var r;
744+
var i;
745+
746+
values = [ 1, 2, 3, 4 ];
747+
expected = [
748+
{
749+
'value': 4,
750+
'done': false
751+
},
752+
{
753+
'value': 3,
754+
'done': false
755+
},
756+
{
757+
'value': 2,
758+
'done': false
759+
},
760+
{
761+
'value': 1,
762+
'done': false
763+
},
764+
{
765+
'value': 4,
766+
'done': false
767+
},
768+
{
769+
'value': 3,
770+
'done': false
771+
},
772+
{
773+
'value': 2,
774+
'done': false
775+
},
776+
{
777+
'done': true
778+
}
779+
];
780+
781+
opts = {
782+
'iter': 7,
783+
'dir': -1
784+
};
785+
it = circarray2iterator( values, opts );
786+
t.equal( it.next.length, 0, 'has zero arity' );
787+
788+
actual = [];
789+
for ( i = 0; i < opts.iter; i++ ) {
790+
r = it.next();
791+
actual.push( r );
792+
t.equal( typeof r.value, 'number', 'returns a number' );
793+
t.equal( typeof r.done, 'boolean', 'returns a boolean' );
794+
}
795+
actual.push( it.next() );
796+
797+
t.deepEqual( actual, expected, 'returns expected values' );
798+
t.end();
799+
});
800+
801+
tape( 'the function supports specifying the iteration direction (callback)', function test( t ) {
802+
var expected;
803+
var actual;
804+
var values;
805+
var opts;
806+
var it;
807+
var r;
808+
var i;
809+
810+
values = [ 1, 2, 3, 4 ];
811+
expected = [
812+
{
813+
'value': 4,
814+
'done': false
815+
},
816+
{
817+
'value': 6,
818+
'done': false
819+
},
820+
{
821+
'value': 6,
822+
'done': false
823+
},
824+
{
825+
'value': 4,
826+
'done': false
827+
},
828+
{
829+
'value': 20,
830+
'done': false
831+
},
832+
{
833+
'value': 18,
834+
'done': false
835+
},
836+
{
837+
'value': 14,
838+
'done': false
839+
},
840+
{
841+
'done': true
842+
}
843+
];
844+
845+
opts = {
846+
'iter': 7,
847+
'dir': -1
848+
};
849+
it = circarray2iterator( values, opts, scale );
850+
t.equal( it.next.length, 0, 'has zero arity' );
851+
852+
actual = [];
853+
for ( i = 0; i < opts.iter; i++ ) {
854+
r = it.next();
855+
actual.push( r );
856+
t.equal( typeof r.value, 'number', 'returns a number' );
857+
t.equal( typeof r.done, 'boolean', 'returns a boolean' );
858+
}
859+
actual.push( it.next() );
860+
861+
t.deepEqual( actual, expected, 'returns expected values' );
862+
t.end();
863+
864+
function scale( v, i, n ) {
865+
return v * n;
866+
}
867+
});
868+
670869
tape( 'the function returns an iterator protocol-compliant object (value+done)', function test( t ) {
671870
var expected;
672871
var values;

0 commit comments

Comments
 (0)