2121// MODULES //
2222
2323var hasOwnProp = require ( '@stdlib/assert/has-own-property' ) ;
24- var objectAssign = require ( '@stdlib/object/assign' ) ;
25- var isObject = require ( '@stdlib/assert/is-object' ) ;
24+ var isPlainObject = require ( '@stdlib/assert/is-plain-object' ) ;
2625var isInteger = require ( '@stdlib/assert/is-integer' ) . isPrimitive ;
2726var isndarrayLike = require ( '@stdlib/assert/is-ndarray-like' ) ;
2827var broadcastScalar = require ( '@stdlib/ndarray/base/broadcast-scalar' ) ;
@@ -44,39 +43,38 @@ var DEFAULT_DTYPE = defaults.get( 'dtypes.integer_index' );
4443// MAIN //
4544
4645/**
47- * Returns the first index of a specified search element along one or more ndarray dimensions and assigns the results to a provided output ndarray.
46+ * Returns the first index of a specified search element along an ndarray dimension and assigns the results to a provided output ndarray.
4847*
49- * @name assign
50- * @type {Function }
5148* @param {ndarrayLike } x - input ndarray
5249* @param {(ndarrayLike|*) } searchElement - search element
5350* @param {(ndarrayLike|integer) } [fromIndex] - index from which to begin searching
5451* @param {ndarrayLike } out - output ndarray
5552* @param {Options } [options] - function options
56- * @param {IntegerArray } [options.dims ] - list of dimensions over which to perform operation
53+ * @param {IntegerArray } [options.dim=-1 ] - dimension over which to perform operation
5754* @throws {TypeError } function must be provided at least three arguments
5855* @throws {TypeError } first argument must be an ndarray-like object
5956* @throws {TypeError } third argument must be either an ndarray-like object or an integer
60- * @throws {TypeError } fourth argument must be an ndarray-like object
57+ * @throws {TypeError } output argument must be an ndarray-like object
6158* @throws {TypeError } options argument must be an object
62- * @throws {RangeError } dimension indices must not exceed input ndarray bounds
63- * @throws {RangeError } number of dimension indices must not exceed the number of input ndarray dimensions
59+ * @throws {RangeError } dimension dimension index must not exceed input ndarray bounds
60+ * @throws {RangeError } first argument must have at least one dimension
6461* @throws {Error } must provide valid options
6562* @returns {ndarray } output ndarray
6663*
6764* @example
6865* var Float64Array = require( '@stdlib/array/float64' );
6966* var zeros = require( '@stdlib/ndarray/zeros' );
67+ * var ndarray2array = require( '@stdlib/ndarray/to-array' );
7068* var ndarray = require( '@stdlib/ndarray/ctor' );
7169*
7270* // Create data buffers:
7371* var xbuf = new Float64Array( [ 1.0, 2.0, -3.0, 4.0, -5.0, 6.0 ] );
7472*
7573* // Define the shape of the input array:
76- * var shape = [ 3, 1, 2 ];
74+ * var shape = [ 2, 3 ];
7775*
7876* // Define the array strides:
79- * var strides = [ 2, 2 , 1 ];
77+ * var strides = [ 3 , 1 ];
8078*
8179* // Define the index offset:
8280* var offset = 0;
@@ -85,91 +83,138 @@ var DEFAULT_DTYPE = defaults.get( 'dtypes.integer_index' );
8583* var x = new ndarray( 'float64', xbuf, shape, strides, offset, 'row-major' );
8684*
8785* // Create an output ndarray:
88- * var y = zeros( [], {
86+ * var y = zeros( [ 2 ], {
8987* 'dtype': 'int32'
9088* });
9189*
9290* // Perform operation:
93- * var out = assign( x, 4 .0, y );
91+ * var out = assign( x, -5 .0, y );
9492* // returns <ndarray>
9593*
9694* var bool = ( out === y );
9795* // returns true
9896*
99- * var idx = out.get( );
100- * // returns 3
97+ * var arr = ndarray2array( out );
98+ * // returns [ -1, 1 ]
10199*/
102- function assign ( x , searchElement , fromIndex , out , options ) {
100+ function assign ( x , searchElement , fromIndex , out ) {
101+ var hasOptions ;
102+ var options ;
103103 var nargs ;
104104 var opts ;
105+ var fidx ;
106+ var iflg ;
105107 var ord ;
106108 var dt ;
107109 var sh ;
110+ var v ;
111+ var o ;
108112
109113 nargs = arguments . length ;
110- if ( nargs < 3 ) {
111- throw new TypeError ( format ( 'invalid argument. The function must be provided at least three arguments. Value: `%s`.' , nargs ) ) ;
112- }
113-
114114 if ( ! isndarrayLike ( x ) ) {
115- throw new TypeError ( format ( 'invalid argument. The first argument must be an ndarray-like object. Value: `%s`.' , x ) ) ;
115+ throw new TypeError ( format ( 'invalid argument. The first argument must be an ndarray. Value: `%s`.' , x ) ) ;
116+ }
117+ if ( nargs < 2 ) {
118+ throw new TypeError ( format ( 'invalid argument. Second argument must be either an ndarray or a scalar value. Value: `%s`.' , searchElement ) ) ;
119+ }
120+ if ( nargs < 3 ) {
121+ throw new TypeError ( format ( 'invalid argument. Third argument must be an ndarray. Value: `%s`.' , fromIndex ) ) ;
116122 }
117-
118123 // Resolve input ndarray meta data:
119124 dt = getDType ( x ) ;
120125 ord = getOrder ( x ) ;
121126
122- // Initialize options object:
123- opts = { } ;
127+ // Initialize an options object:
128+ opts = {
129+ 'dims' : [ - 1 ] // default behavior is to perform a reduction over the last dimension
130+ } ;
131+
132+ // Initialize the `fromIndex` to the first element along a dimension:
133+ fidx = 0 ;
134+
135+ // Initialize a flag indicating whether the `fromIndex` argument is a scalar:
136+ iflg = true ;
137+
138+ // Initialize a flag indicating whether an `options` argument was provided:
139+ hasOptions = false ;
124140
125- // Case: assign( x, searchElement , out )
141+ // Case: assign( x, search_element , out )
126142 if ( nargs === 3 ) {
127- out = fromIndex ;
128- fromIndex = 0 ;
143+ o = fromIndex ;
129144 }
130- // Case: assign( x, searchElement, out, options )
131- else if (
132- nargs === 4 &&
133- isndarrayLike ( fromIndex ) &&
134- ! isndarrayLike ( out ) &&
135- isObject ( out )
136- ) {
137- opts = objectAssign ( opts , out ) ;
138- out = fromIndex ;
139- fromIndex = 0 ;
145+ // Case: assign( x, search_element, ???, ??? )
146+ else if ( nargs === 4 ) {
147+ // Case: assign( x, search_element, from_index, out )
148+ if ( isndarrayLike ( out ) ) {
149+ o = out ;
150+
151+ // Case: assign( x, search_element, from_index_scalar, out )
152+ if ( isInteger ( fromIndex ) ) {
153+ fidx = fromIndex ;
154+ }
155+ // Case: assign( x, search_element, from_index_ndarray, out )
156+ else if ( isndarrayLike ( fromIndex ) ) {
157+ fidx = fromIndex ;
158+ iflg = false ;
159+ }
160+ // Case: assign( x, search_element, ???, out )
161+ else {
162+ throw new TypeError ( format ( 'invalid argument. Third argument must be either an ndarray or an integer. Value: `%s`.' , fromIndex ) ) ;
163+ }
164+ }
165+ // Case: assign( x, search_element, out, options )
166+ else {
167+ o = fromIndex ;
168+ options = out ;
169+ hasOptions = true ;
170+ }
140171 }
141- // Case: assign( x, searchElement, fromIndex, out, options )
142- else if ( nargs === 5 ) {
143- if ( ! isObject ( options ) ) {
144- throw new TypeError ( format ( 'invalid argument. The fifth argument must be an object. Value: `%s`.' , options ) ) ;
172+ // Case: assign( x, search_element, from_index, out, options )
173+ else { // nargs > 4
174+ // Case: assign( x, search_element, from_index_scalar, out, options )
175+ if ( isInteger ( fromIndex ) ) {
176+ fidx = fromIndex ;
177+ }
178+ // Case: assign( x, search_element, from_index_ndarray, out, options )
179+ else if ( isndarrayLike ( fromIndex ) ) {
180+ fidx = fromIndex ;
181+ iflg = false ;
145182 }
146- opts = objectAssign ( opts , options ) ;
183+ // Case: assign( x, search_element, ???, out, options )
184+ else {
185+ throw new TypeError ( format ( 'invalid argument. Third argument must be either an ndarray or an integer. Value: `%s`.' , fromIndex ) ) ;
186+ }
187+ o = out ;
188+ options = arguments [ 4 ] ;
189+ hasOptions = true ;
147190 }
148-
149- // Resolve shape for broadcasting
150- if ( hasOwnProp ( opts , 'dims' ) ) {
151- sh = nonCoreShape ( getShape ( x ) , opts . dims ) ;
152- } else {
153- sh = [ ] ;
191+ if ( hasOptions && ! isPlainObject ( options ) ) {
192+ throw new TypeError ( format ( 'invalid argument. Options argument must be an object. Value: `%s`.' , options ) ) ;
193+ }
194+ // Resolve provided options...
195+ if ( hasOwnProp ( options , 'dim' ) ) {
196+ opts . dims [ 0 ] = options . dim ;
197+ }
198+ // Resolve the list of non-reduced dimensions:
199+ sh = getShape ( x ) ;
200+ if ( sh . length < 1 ) {
201+ throw new RangeError ( 'invalid argument. First argument must have at least one dimension.' ) ;
154202 }
203+ sh = nonCoreShape ( sh , opts . dims ) ;
155204
156- // Normalize search element:
205+ // Broadcast the search element to match the shape of the non-reduced dimensions...
157206 if ( isndarrayLike ( searchElement ) ) {
158- searchElement = maybeBroadcastArray ( searchElement , sh ) ;
207+ v = maybeBroadcastArray ( searchElement , sh ) ;
159208 } else {
160- searchElement = broadcastScalar ( searchElement , dt , sh , ord ) ;
209+ v = broadcastScalar ( searchElement , dt , sh , ord ) ; // WARNING: potential for undesired value casting (e.g., if `searchElement` is `null` and cast to `float64`, the broadcasted scalar will be `0`, not `null`!)
161210 }
162-
163- // Normalize from index:
164- if ( isndarrayLike ( fromIndex ) ) {
165- fromIndex = maybeBroadcastArray ( fromIndex , sh ) ;
166- } else if ( isInteger ( fromIndex ) ) {
167- fromIndex = broadcastScalar ( fromIndex , DEFAULT_DTYPE , sh , ord ) ;
211+ // Broadcast the `fromIndex` to match the shape of the non-reduced dimensions...
212+ if ( iflg ) {
213+ fidx = broadcastScalar ( fidx , DEFAULT_DTYPE , sh , ord ) ;
168214 } else {
169- throw new TypeError ( format ( 'invalid argument. Third argument must be either an ndarray or an integer. Value: `%s`.' , fromIndex ) ) ;
215+ fidx = maybeBroadcastArray ( fidx , sh ) ;
170216 }
171-
172- return base ( x , searchElement , fromIndex , out , opts ) ;
217+ return base ( x , v , fidx , o , opts ) ;
173218}
174219
175220
0 commit comments