main.js (4362B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2020 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 'use strict'; 20 21 // MODULES // 22 23 var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); 24 var isFunction = require( '@stdlib/assert/is-function' ); 25 var isIteratorLike = require( '@stdlib/assert/is-iterator-like' ); 26 var isNumber = require( '@stdlib/assert/is-number' ).isPrimitive; 27 var hasOwnProp = require( '@stdlib/assert/has-own-property' ); 28 var iteratorSymbol = require( '@stdlib/symbol/iterator' ); 29 var validate = require( './validate.js' ); 30 31 32 // MAIN // 33 34 /** 35 * Returns an iterator which invokes a unary function accepting a single numeric argument for each iterated value. 36 * 37 * ## Notes 38 * 39 * - When invoked, the input function is provided one argument: 40 * 41 * - `value`: iterated value 42 * 43 * - If an environment supports `Symbol.iterator` **and** a provided iterator is iterable, the returned iterator is iterable. 44 * 45 * @param {Iterator} iterator - input iterator 46 * @param {Function} fcn - function to invoke 47 * @param {Options} [options] - options 48 * @param {*} [options.invalid=NaN] - return value when an input iterator yields a non-numeric value 49 * @throws {TypeError} first argument must be an iterator protocol-compliant object 50 * @throws {TypeError} second argument must be a function 51 * @throws {TypeError} options argument must be an object 52 * @throws {TypeError} must provide valid options 53 * @returns {Iterator} iterator 54 * 55 * @example 56 * var randu = require( '@stdlib/random/iter/randu' ); 57 * var sin = require( '@stdlib/math/base/special/sin' ); 58 * 59 * var iter = iterMap( randu(), sin ); 60 * 61 * var r = iter.next().value; 62 * // returns <number> 63 * 64 * r = iter.next().value; 65 * // returns <number> 66 * 67 * r = iter.next().value; 68 * // returns <number> 69 * 70 * // ... 71 */ 72 function iterMap( iterator, fcn, options ) { 73 var opts; 74 var iter; 75 var err; 76 var FLG; 77 if ( !isIteratorLike( iterator ) ) { 78 throw new TypeError( 'invalid argument. First argument must be an iterator protocol-compliant object. Value: `' + iterator + '`.' ); 79 } 80 if ( !isFunction( fcn ) ) { 81 throw new TypeError( 'invalid argument. Second argument must be a function. Value: `' + fcn + '`.' ); 82 } 83 opts = { 84 'invalid': NaN 85 }; 86 if ( arguments.length > 2 ) { 87 err = validate( opts, options ); 88 if ( err ) { 89 throw err; 90 } 91 } 92 // Create an iterator protocol-compliant object: 93 iter = {}; 94 setReadOnly( iter, 'next', next ); 95 setReadOnly( iter, 'return', end ); 96 97 // If an environment supports `Symbol.iterator` and the provided iterator is iterable, make the iterator iterable: 98 if ( iteratorSymbol && isFunction( iterator[ iteratorSymbol ] ) ) { 99 setReadOnly( iter, iteratorSymbol, factory ); 100 } 101 return iter; 102 103 /** 104 * Returns an iterator protocol-compliant object containing the next iterated value. 105 * 106 * @private 107 * @returns {Object} iterator protocol-compliant object 108 */ 109 function next() { 110 var out; 111 var v; 112 if ( FLG ) { 113 return { 114 'done': true 115 }; 116 } 117 v = iterator.next(); 118 if ( v.done ) { 119 FLG = true; 120 out = {}; 121 if ( hasOwnProp( v, 'value' ) ) { 122 out.value = ( isNumber( v.value ) ) ? fcn( v.value ) : opts.invalid; // eslint-disable-line max-len 123 } 124 out.done = true; 125 return out; 126 } 127 return { 128 'value': ( isNumber( v.value ) ) ? fcn( v.value ) : opts.invalid, 129 'done': false 130 }; 131 } 132 133 /** 134 * Finishes an iterator. 135 * 136 * @private 137 * @param {*} [value] - value to return 138 * @returns {Object} iterator protocol-compliant object 139 */ 140 function end( value ) { 141 FLG = true; 142 if ( arguments.length ) { 143 return { 144 'value': value, 145 'done': true 146 }; 147 } 148 return { 149 'done': true 150 }; 151 } 152 153 /** 154 * Returns a new iterator. 155 * 156 * @private 157 * @returns {Iterator} iterator 158 */ 159 function factory() { 160 return iterMap( iterator[ iteratorSymbol ](), fcn, opts ); 161 } 162 } 163 164 165 // EXPORTS // 166 167 module.exports = iterMap;