diff --git a/CHANGELOG.md b/CHANGELOG.md index c2d67707..594e3fe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@
-## Unreleased (2024-06-20) +## Unreleased (2024-06-21)
@@ -20,6 +20,7 @@ ##### Features +- [`82a217c`](https://github.com/stdlib-js/stdlib/commit/82a217c79f7c10d4aca129fbaba753ce5055115a) - add `place` to namespace - [`cd14ed3`](https://github.com/stdlib-js/stdlib/commit/cd14ed3f1bbc7f0cc7dc55d155e6fa86c90adb23) - update namespace TypeScript declarations [(#2402)](https://github.com/stdlib-js/stdlib/pull/2402) - [`e1993a6`](https://github.com/stdlib-js/stdlib/commit/e1993a6ee84bd212b5a00210e360b14e2a979f3a) - add `put` to namespace - [`3edcfe5`](https://github.com/stdlib-js/stdlib/commit/3edcfe5d814fd12a56dbe492ddc78663721f5acd) - update namespace TypeScript declarations [(#2303)](https://github.com/stdlib-js/stdlib/pull/2303) @@ -1721,6 +1722,28 @@ This release closes the following issue: +
+ +#### [@stdlib/array/place](https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/array/place) + +
+ +
+ +##### Features + +- [`7b97e18`](https://github.com/stdlib-js/stdlib/commit/7b97e18463ee395851cb173559793a9d2a7c37e3) - add `array/place` + +
+ + + +
+ +
+ + +
#### [@stdlib/array/promotion-rules](https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/array/promotion-rules) @@ -2116,6 +2139,8 @@ A total of 13 people contributed to this release. Thank you to the following con
+- [`82a217c`](https://github.com/stdlib-js/stdlib/commit/82a217c79f7c10d4aca129fbaba753ce5055115a) - **feat:** add `place` to namespace _(by Athan Reines)_ +- [`7b97e18`](https://github.com/stdlib-js/stdlib/commit/7b97e18463ee395851cb173559793a9d2a7c37e3) - **feat:** add `array/place` _(by Athan Reines)_ - [`00fd68d`](https://github.com/stdlib-js/stdlib/commit/00fd68ddf80a71b08e5353c63c297fca4daaf873) - **feat:** update namespace TypeScript declarations [(#2415)](https://github.com/stdlib-js/stdlib/pull/2415) _(by stdlib-bot, Athan Reines)_ - [`0b3db04`](https://github.com/stdlib-js/stdlib/commit/0b3db0401bd16df7aeccb500d8280c280a394762) - **feat:** add `toSorted` method to `array/bool` [(#2413)](https://github.com/stdlib-js/stdlib/pull/2413) _(by Jaysukh Makvana)_ - [`e57ccb2`](https://github.com/stdlib-js/stdlib/commit/e57ccb234687de4087aa12348d266ea448f3f241) - **refactor:** update boolean array indexing implementation and add tests _(by Athan Reines)_ diff --git a/lib/index.js b/lib/index.js index 19f4f14f..8547623d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -436,6 +436,15 @@ setReadOnly( ns, 'ones', require( './../ones' ) ); */ setReadOnly( ns, 'onesLike', require( './../ones-like' ) ); +/** +* @name place +* @memberof ns +* @readonly +* @type {Function} +* @see {@link module:@stdlib/array/place} +*/ +setReadOnly( ns, 'place', require( './../place' ) ); + /** * @name typedarraypool * @memberof ns diff --git a/place/README.md b/place/README.md new file mode 100644 index 00000000..36a086b4 --- /dev/null +++ b/place/README.md @@ -0,0 +1,165 @@ + + +# place + +> Replace elements of an array with provided values according to a provided mask array. + +
+ +## Usage + +```javascript +var place = require( '@stdlib/array/place' ); +``` + +#### place( x, mask, values\[, options] ) + +Replaces elements of an array with provided values according to a provided mask array. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var out = place( x, [ 0, 1, 0, 1 ], [ 20, 40 ] ); +// returns [ 1, 20, 3, 40 ] + +var bool = ( out === x ); +// returns true +``` + +The function supports the following parameters: + +- **x**: input array. +- **mask**: mask array. +- **values**: values to set. +- **options**: function options. + +The function supports the following options: + +- **mode**: string specifying behavior when the number of `values` does not equal the number of truthy `mask` values. Default: `'repeat'`. + +The function supports the following modes: + +- `'strict'`: specifies that the function must raise an exception when the number of `values` does not **exactly** equal the number of truthy `mask` values. +- `'non_strict'`: specifies that the function must raise an exception when the function is provided insufficient `values` to satisfy the `mask` array. +- `'strict_broadcast'`: specifies that the function must broadcast a single-element `values` array and otherwise raise an exception when the number of `values` does not **exactly** equal the number of truthy `mask` values. +- `'broadcast'`: specifies that the function must broadcast a single-element `values` array and otherwise raise an exception when the function is provided insufficient `values` to satisfy the `mask` array. +- `'repeat'`: specifies that the function must reuse provided `values` when replacing elements in `x` in order to satisfy the `mask` array. + +In broadcasting modes, the function supports broadcasting a `values` array containing a single element against the number of truthy values in the `mask` array. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var out = place( x, [ 0, 1, 0, 1 ], [ 20 ], { + 'mode': 'strict_broadcast' +}); +// returns [ 1, 20, 3, 20 ] + +var bool = ( out === x ); +// returns true +``` + +In repeat mode, the function supports recycling elements in a `values` array to satisfy the number of truthy values in the `mask` array. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var out = place( x, [ 1, 1, 0, 1 ], [ 20, 40 ], { + 'mode': 'repeat' +}); +// returns [ 20, 40, 3, 20 ] + +var bool = ( out === x ); +// returns true +``` + +
+ + + +
+ +## Notes + +- The function mutates the input array `x`. +- If a `mask` array element is truthy, the corresponding element in `x` is **replaced**; otherwise, the corresponding element in `x` is "masked" and thus left unchanged. +- The `values` array must have a [data type][@stdlib/array/dtypes] which can be [safely cast][@stdlib/array/safe-casts] to the input array data type. Floating-point data types (both real and complex) are allowed to downcast to a lower precision data type of the [same kind][@stdlib/array/same-kind-casts] (e.g., element values from a `'float64'` values array can be assigned to corresponding elements in a `'float32'` input array). + +
+ + + +
+ +## Examples + + + +```javascript +var filledBy = require( '@stdlib/array/base/filled-by' ); +var discreteUniform = require( '@stdlib/random/base/discrete-uniform' ); +var bernoulli = require( '@stdlib/random/base/bernoulli' ); +var linspace = require( '@stdlib/array/base/linspace' ); +var place = require( '@stdlib/array/place' ); + +// Generate a linearly spaced array: +var x = linspace( 0, 100, 11 ); +console.log( x ); + +// Generate a random mask array: +var N = discreteUniform( 5, 15 ); +var mask = filledBy( N, bernoulli.factory( 0.3 ) ); +console.log( mask ); + +// Generate an array of random values: +var values = filledBy( N, discreteUniform.factory( 1000, 2000 ) ); +console.log( values ); + +// Update a random sample of elements in `x`: +var out = place( x, mask, values ); +console.log( out ); +``` + +
+ + + + + + + + + + + + + + diff --git a/place/benchmark/benchmark.js b/place/benchmark/benchmark.js new file mode 100644 index 00000000..95280c5d --- /dev/null +++ b/place/benchmark/benchmark.js @@ -0,0 +1,79 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var bench = require( '@stdlib/bench' ); +var isArray = require( '@stdlib/assert/is-array' ); +var zeroTo = require( './../../base/zero-to' ); +var ones = require( './../../base/ones' ); +var pkg = require( './../package.json' ).name; +var place = require( './../lib' ); + + +// MAIN // + +bench( pkg+'::no_broadcasting:len=100', function benchmark( b ) { + var mask; + var x; + var i; + var v; + + x = zeroTo( 100 ); + mask = ones( 100 ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = place( x, mask, x ); + if ( typeof v !== 'object' ) { + b.fail( 'should return an array' ); + } + } + b.toc(); + if ( !isArray( v ) ) { + b.fail( 'should return an array' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+'::broadcasting:len=100', function benchmark( b ) { + var mask; + var x; + var i; + var v; + + x = zeroTo( 100 ); + mask = ones( 100 ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = place( x, mask, [ i ] ); + if ( typeof v !== 'object' ) { + b.fail( 'should return an array' ); + } + } + b.toc(); + if ( !isArray( v ) ) { + b.fail( 'should return an array' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/place/benchmark/benchmark.length.js b/place/benchmark/benchmark.length.js new file mode 100644 index 00000000..0d1b089c --- /dev/null +++ b/place/benchmark/benchmark.length.js @@ -0,0 +1,113 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var bench = require( '@stdlib/bench' ); +var pow = require( '@stdlib/math/base/special/pow' ); +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var ones = require( './../../base/ones' ); +var isArray = require( '@stdlib/assert/is-array' ); +var pkg = require( './../package.json' ).name; +var place = require( './../lib' ); + + +// VARIABLES // + +var opts = { + 'dtype': 'generic' +}; + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} len - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( len ) { + var values; + var mask; + var x; + + x = discreteUniform( len, 0, 10, opts ); + mask = ones( len ); + values = [ + discreteUniform( len, -10, 0, opts ), + discreteUniform( len, 0, 10, opts ) + ]; + + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var v; + var i; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = place( x, mask, values[ i%values.length ] ); + if ( typeof v !== 'object' ) { + b.fail( 'should return an array' ); + } + } + b.toc(); + if ( !isArray( v ) ) { + b.fail( 'should return an array' ); + } + b.pass( 'benchmark finished' ); + b.end(); + } +} + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var len; + var min; + var max; + var f; + var i; + + min = 1; // 10^min + max = 6; // 10^max + + for ( i = min; i <= max; i++ ) { + len = pow( 10, i ); + f = createBenchmark( len ); + bench( pkg+':len='+len, f ); + } +} + +main(); diff --git a/place/docs/repl.txt b/place/docs/repl.txt new file mode 100644 index 00000000..e9816ac3 --- /dev/null +++ b/place/docs/repl.txt @@ -0,0 +1,68 @@ + +{{alias}}( x, mask, values[, options] ) + Replaces elements of an array with provided values according to a provided + mask array. + + In broadcasting modes, the function supports broadcasting a values array + containing a single element against the number of truthy values in the mask + array. + + In repeat mode, the function supports recycling elements in a values array + to satisfy the number of truthy values in the mask array. + + The function mutates the input array. + + Parameters + ---------- + x: ArrayLikeObject + Input array. + + mask: ArrayLikeObject + Mask array. If a mask array element is truthy, the corresponding element + in `x` is *replaced*; otherwise, the corresponding element in `x` is + "masked" and thus left unchanged. + + values: ArrayLikeObject + Values to set. + + options: Object (optional) + Function options. + + options.mode: string (optional) + String specifying behavior when the number of values to set does not + equal the number of truthy mask values. The function supports the + following modes: + + - 'strict': specifies that the function must raise an exception when the + number of values does not *exactly* equal the number of truthy mask + values. + - 'non_strict': specifies that the function must raise an exception when + the function is provided insufficient values to satisfy the mask array. + - 'strict_broadcast': specifies that the function must broadcast a + single-element values array and otherwise raise an exception when the + number of values does not **exactly** equal the number of truthy mask + values. + - 'broadcast': specifies that the function must broadcast a single- + element values array and otherwise raise an exception when the function + is provided insufficient values to satisfy the mask array. + - 'repeat': specifies that the function must reuse provided values when + replacing elements in `x` in order to satisfy the mask array. + + Default: 'repeat'. + + Returns + ------- + out: ArrayLikeObject + Input array. + + Examples + -------- + > var x = [ 1, 2, 3, 4 ]; + > var out = {{alias}}( x, [ 0, 1, 0, 1 ], [ 20, 40 ] ) + [ 1, 20, 3, 40 ] + > var bool = ( out === x ) + true + + See Also + -------- + diff --git a/place/docs/types/index.d.ts b/place/docs/types/index.d.ts new file mode 100644 index 00000000..76264b76 --- /dev/null +++ b/place/docs/types/index.d.ts @@ -0,0 +1,232 @@ +/* +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// TypeScript Version: 4.1 + +/// + +import { Collection, AccessorArrayLike, TypedArray, ComplexTypedArray, BooleanTypedArray } from '@stdlib/types/array'; +import { ComplexLike } from '@stdlib/types/complex'; + +/** +* Mask array. +*/ +type MaskArray = Collection | AccessorArrayLike; + +/** +* Values array. +*/ +type ValuesArray = Collection | AccessorArrayLike; + +/** +* Interface describing function options. +*/ +interface Options { + /** + * Mode specifying behavior when the number of values to set does not equal the number of truthy values in the mask array. + * + * ## Notes + * + * - The function supports the following modes: + * + * - `'strict'`: specifies that the function must raise an exception when the number of `values` does not **exactly** equal the number of truthy `mask` values. + * - `'non_strict'`: specifies that the function must raise an exception when the function is provided insufficient `values` to satisfy the `mask` array. + * - `'strict_broadcast'`: specifies that the function must broadcast a single-element `values` array and otherwise raise an exception when the number of `values` does not **exactly** equal the number of truthy `mask` values. + * - `'broadcast'`: specifies that the function must broadcast a single-element `values` array and otherwise raise an exception when the function is provided insufficient `values` to satisfy the `mask` array. + * - `'repeat'`: specifies that the function must reuse provided `values` when replacing elements in `x` in order to satisfy the `mask` array. + */ + mode?: 'strict' | 'non_strict' | 'strict_broadcast' | 'broadcast' | 'repeat'; +} + +/** +* Replaces elements of an array with provided values according to a provided mask array. +* +* @param x - input array +* @param mask - mask array +* @param values - values to set +* @param options - function options +* @returns input array +* +* @example +* var Int32Array = require( '@stdlib/array/int32' ); +* +* var x = new Int32Array( [ 1, 2, 3, 4 ] ); +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = [ 20, 30 ]; +* +* var out = place( x, mask, values ); +* // returns [ 1, 20, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +* +* @example +* var Int32Array = require( '@stdlib/array/int32' ); +* +* var x = new Int32Array( [ 1, 2, 3, 4 ] ); +* +* var out = place( x, [ 0, 1, 1, 0 ], [ 30 ] ); +* // returns [ 1, 30, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +*/ +declare function place( x: T, mask: MaskArray, values: ValuesArray, options?: Options ): T; + +/** +* Replaces elements of an array with provided values according to a provided mask array. +* +* @param x - input array +* @param mask - mask array +* @param values - values to set +* @param options - function options +* @returns input array +* +* @example +* var Complex128Array = require( '@stdlib/array/complex128' ); +* +* var x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = new Complex128Array( [ 20.0, 30.0, 40, 5.0 ] ); +* +* var out = place( x, mask, values ); +* // returns +* +* var bool = ( out === x ); +* // returns true +* +* @example +* var Complex128Array = require( '@stdlib/array/complex128' ); +* +* var x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = new Complex128Array( [ 20.0, 30.0 ] ); +* +* var out = place( x, mask, values ); +* // returns +* +* var bool = ( out === x ); +* // returns true +*/ +declare function place( x: T, mask: MaskArray, values: ValuesArray, options?: Options ): T; + +/** +* Replaces elements of an array with provided values according to a provided mask array. +* +* @param x - input array +* @param mask - mask array +* @param values - values to set +* @param options - function options +* @returns input array +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = [ 20, 30 ]; +* +* var out = place( x, mask, values ); +* // returns [ 1, 20, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* +* var out = place( x, [ 0, 1, 1, 0 ], [ 30 ] ); +* // returns [ 1, 30, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +*/ +declare function place( x: Array, mask: MaskArray, values: ValuesArray, options?: Options ): Array; + +/** +* Replaces elements of an array with provided values according to a provided mask array. +* +* @param x - input array +* @param mask - mask array +* @param values - values to set +* @param options - function options +* @returns input array +* +* @example +* var toAccessorArray = require( '@stdlib/array/base/to-accessor-array' ); +* +* var x = toAccessorArray( [ 1, 2, 3, 4 ] ); +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = [ 20, 30 ]; +* +* var out = place( x, mask, values ); +* +* var bool = ( out === x ); +* // returns true +* +* @example +* var toAccessorArray = require( '@stdlib/array/base/to-accessor-array' ); +* +* var x = toAccessorArray( [ 1, 2, 3, 4 ] ); +* +* var out = place( x, [ 0, 1, 1, 0 ], [ 30 ] ); +* +* var bool = ( out === x ); +* // returns true +*/ +declare function place( x: AccessorArrayLike, mask: MaskArray, values: ValuesArray, options?: Options ): AccessorArrayLike; + +/** +* Replaces elements of an array with provided values according to a provided mask array. +* +* @param x - input array +* @param mask - mask array +* @param values - values to set +* @param options - function options +* @returns input array +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = [ 20, 30 ]; +* +* var out = place( x, mask, values ); +* // returns [ 1, 20, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* +* var out = place( x, [ 0, 1, 1, 0 ], [ 30 ] ); +* // returns [ 1, 30, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +*/ +declare function place( x: Collection, mask: MaskArray, values: ValuesArray, options?: Options ): Collection; + + +// EXPORTS // + +export = place; diff --git a/place/docs/types/test.ts b/place/docs/types/test.ts new file mode 100644 index 00000000..3b2139ba --- /dev/null +++ b/place/docs/types/test.ts @@ -0,0 +1,96 @@ +/* +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Complex128Array = require( './../../../complex128' ); +import Complex64Array = require( './../../../complex64' ); +import Complex128 = require( '@stdlib/complex/float64' ); +import AccessorArray = require( './../../../base/accessor' ); +import place = require( './index' ); + + +// TESTS // + +// The function returns an array... +{ + place( [ 1, 2, 3, 4 ], [ 1, 0, 0, 1 ], [ 20, 30 ] ); // $ExpectType number[] + place( new Int32Array( [ 1, 2, 3, 4 ] ), [ 1, 0, 0, 1 ], [ 20, 30 ] ); // $ExpectType Int32Array + place( new Complex128Array( [ 1, 2, 3, 4 ] ), [ 1, 0, 0, 1 ], [ new Complex128( 20, 30 ), [ 40, 50 ] ] ); // $ExpectType Complex128Array + place( new Complex64Array( [ 1, 2, 3, 4 ] ), [ 1, 0, 0, 1 ], [ new Complex128( 20, 30 ), [ 40, 50 ] ] ); // $ExpectType Complex64Array + place( new AccessorArray( [ 1, 2, 3, 4 ] ), [ 1, 0, 0, 1 ], new AccessorArray( [ 20, 30 ] ) ); // $ExpectType AccessorArrayLike +} + +// The compiler throws an error if the function is provided a first argument which is not an array-like object... +{ + place( 1, [ 1, 0, 0, 1 ], [ 20, 30 ] ); // $ExpectError + place( true, [ 1, 0, 0, 1 ], [ 20, 30 ] ); // $ExpectError + place( false, [ 1, 0, 0, 1 ], [ 20, 30 ] ); // $ExpectError + place( null, [ 1, 0, 0, 1 ], [ 20, 30 ] ); // $ExpectError + place( void 0, [ 1, 0, 0, 1 ], [ 20, 30 ] ); // $ExpectError + place( {}, [ 1, 0, 0, 1 ], [ 20, 30 ] ); // $ExpectError +} + +// The compiler throws an error if the function is provided a second argument which is not an array-like object... +{ + place( [], 1, [ 20, 30 ] ); // $ExpectError + place( [], true, [ 20, 30 ] ); // $ExpectError + place( [], false, [ 20, 30 ] ); // $ExpectError + place( [], null, [ 20, 30 ] ); // $ExpectError + place( [], void 0, [ 20, 30 ] ); // $ExpectError + place( [], {}, [ 20, 30 ] ); // $ExpectError +} + +// The compiler throws an error if the function is provided a third argument which is not an array-like object... +{ + place( [], [ 1, 0, 0, 1 ], 1 ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], true ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], false ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], null ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], void 0 ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], {} ); // $ExpectError +} + +// The compiler throws an error if the function is provided a fourth argument which is not an object... +{ + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], '1' ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], 1 ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], true ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], false ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], null ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], [] ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the function is provided a `mode` option which is not a valid mode... +{ + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], { 'mode': '1' } ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], { 'mode': 1 } ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], { 'mode': true } ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], { 'mode': false } ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], { 'mode': null } ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], { 'mode': {} } ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], { 'mode': [] } ); // $ExpectError + place( [], [ 1, 0, 0, 1 ], [ 20, 30 ], { 'mode': ( x: number ): number => x } ); // $ExpectError +} + +// The compiler throws an error if the function is provided an unsupported number of arguments... +{ + place(); // $ExpectError + place( [] ); // $ExpectError + place( [], [] ); // $ExpectError + place( [], [], [], {}, {} ); // $ExpectError +} diff --git a/place/examples/index.js b/place/examples/index.js new file mode 100644 index 00000000..23c6b3bd --- /dev/null +++ b/place/examples/index.js @@ -0,0 +1,42 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var filledBy = require( './../../base/filled-by' ); +var discreteUniform = require( '@stdlib/random/base/discrete-uniform' ); +var bernoulli = require( '@stdlib/random/base/bernoulli' ); +var linspace = require( './../../base/linspace' ); +var place = require( './../lib' ); + +// Generate a linearly spaced array: +var x = linspace( 0, 100, 11 ); +console.log( x ); + +// Generate a random mask array: +var N = discreteUniform( 5, 15 ); +var mask = filledBy( N, bernoulli.factory( 0.3 ) ); +console.log( mask ); + +// Generate an array of random values: +var values = filledBy( N, discreteUniform.factory( 1000, 2000 ) ); +console.log( values ); + +// Update a random sample of elements in `x`: +var out = place( x, mask, values ); +console.log( out ); diff --git a/place/lib/defaults.js b/place/lib/defaults.js new file mode 100644 index 00000000..ae985155 --- /dev/null +++ b/place/lib/defaults.js @@ -0,0 +1,42 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MAIN // + +/** +* Returns default options. +* +* @private +* @returns {Object} default options +* +* @example +* var o = defaults(); +* // returns {...} +*/ +function defaults() { + return { + 'mode': 'repeat' + }; +} + + +// EXPORTS // + +module.exports = defaults; diff --git a/place/lib/index.js b/place/lib/index.js new file mode 100644 index 00000000..06db07c3 --- /dev/null +++ b/place/lib/index.js @@ -0,0 +1,48 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Replace elements of an array with provided values according to a provided mask array. +* +* @module @stdlib/array/place +* +* @example +* var place = require( '@stdlib/array/place' ); +* +* var x = [ 1, 2, 3, 4 ]; +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = [ 20, 30 ]; +* +* var out = place( x, mask, values ); +* // returns [ 1, 20, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/place/lib/main.js b/place/lib/main.js new file mode 100644 index 00000000..ab33a910 --- /dev/null +++ b/place/lib/main.js @@ -0,0 +1,130 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var isMostlySafeCast = require( './../../base/assert/is-mostly-safe-data-type-cast' ); +var isRealDataType = require( './../../base/assert/is-real-data-type' ); +var isComplexDataType = require( './../../base/assert/is-complex-floating-point-data-type' ); +var isCollection = require( '@stdlib/assert/is-collection' ); +var base = require( './../../base/place' ); +var dtype = require( './../../dtype' ); +var convert = require( './../../convert' ); +var format = require( '@stdlib/string/format' ); +var defaults = require( './defaults.js' ); +var validate = require( './validate.js' ); + + +// MAIN // + +/** +* Replaces elements of an array with provided values according to a provided mask array. +* +* @param {Collection} x - input array +* @param {Collection} mask - mask array +* @param {Collection} values - values to set +* @param {Options} [options] - function options +* @param {string} [options.mode='repeat'] - string specifying behavior when the number of values does not equal the number of truthy mask values +* @throws {TypeError} first argument must be a collection +* @throws {TypeError} second argument must be a collection +* @throws {TypeError} third argument must be a collection +* @throws {TypeError} options argument must be an object +* @throws {Error} must provide valid options +* @throws {Error} insufficient values to satisfy mask array +* @throws {Error} number of values does not equal the number of truthy mask values +* @throws {TypeError} third argument cannot be safely cast to the data type of the first argument +* @returns {Collection} input array +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = [ 20, 30 ]; +* +* var out = place( x, mask, values ); +* // returns [ 1, 20, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* +* var mask = [ 0, 1, 1, 0 ]; +* var values = [ 30 ]; +* +* var out = place( x, mask, values ); +* // returns [ 1, 30, 30, 4 ] +* +* var bool = ( out === x ); +* // returns true +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* +* var mask = [ 1, 1, 0, 1 ]; +* var values = [ 20, 30 ]; +* +* var out = place( x, mask, values ); +* // returns [ 20, 30, 3, 20 ] +* +* var bool = ( out === x ); +* // returns true +*/ +function place( x, mask, values ) { + var opts; + var err; + var xdt; + var vdt; + if ( !isCollection( x ) ) { + throw new TypeError( format( 'invalid argument. First argument must be an array-like object. Value: `%s`.', x ) ); + } + if ( !isCollection( mask ) ) { + throw new TypeError( format( 'invalid argument. Second argument must be an array-like object. Value: `%s`.', mask ) ); + } + if ( !isCollection( values ) ) { + throw new TypeError( format( 'invalid argument. Third argument must be an array-like object. Value: `%s`.', values ) ); + } + opts = defaults(); + if ( arguments.length > 3 ) { + err = validate( opts, arguments[ 3 ] ); + if ( err ) { + throw err; + } + } + xdt = dtype( x ) || 'generic'; + vdt = dtype( values ) || 'generic'; + + // Safe casts are always allowed and allow same kind casts (i.e., downcasts) only when the input array data type is floating-point... + if ( !isMostlySafeCast( vdt, xdt ) ) { + throw new TypeError( format( 'invalid argument. Third argument cannot be safely cast to the input array data type. Data types: [%s, %s].', vdt, xdt ) ); + } + // When performing a real-to-complex assignment, interpret the real-valued array as containing real components with implied imaginary components equal to zero and explicitly convert to a complex-valued array... + if ( isComplexDataType( xdt ) && isRealDataType( vdt ) ) { + values = convert( values, xdt ); + } + // Replace values in the input array: + return base( x, mask, values, opts.mode ); +} + + +// EXPORTS // + +module.exports = place; diff --git a/place/lib/validate.js b/place/lib/validate.js new file mode 100644 index 00000000..acb45951 --- /dev/null +++ b/place/lib/validate.js @@ -0,0 +1,78 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var isObject = require( '@stdlib/assert/is-plain-object' ); +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var contains = require( './../../base/assert/contains' ).factory; +var format = require( '@stdlib/string/format' ); + + +// VARIABLES // + +var MODES = [ + 'strict', + 'non_strict', + 'strict_broadcast', + 'broadcast', + 'repeat' +]; +var isMode = contains( MODES ); + + +// MAIN // + +/** +* Validates function options. +* +* @private +* @param {Object} opts - destination object +* @param {Options} options - function options +* @param {string} [options.mode] - mode option +* @returns {(Error|null)} null or an error object +* +* @example +* var opts = {}; +* var options = { +* 'mode': 'strict' +* }; +* var err = validate( opts, options ); +* if ( err ) { +* throw err; +* } +*/ +function validate( opts, options ) { + if ( !isObject( options ) ) { + return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); + } + if ( hasOwnProp( options, 'mode' ) ) { + opts.mode = options.mode; + if ( !isMode( opts.mode ) ) { + return new TypeError( format( 'invalid option. `%s` option must be one of the following: "%s". Option: `%s`.', 'mode', MODES.join( '", "' ), opts.mode ) ); + } + } + return null; +} + + +// EXPORTS // + +module.exports = validate; diff --git a/place/package.json b/place/package.json new file mode 100644 index 00000000..98127b6c --- /dev/null +++ b/place/package.json @@ -0,0 +1,65 @@ +{ + "name": "@stdlib/array/place", + "version": "0.0.0", + "description": "Replace elements of an array with provided values according to a provided mask array.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "main": "./lib", + "directories": { + "benchmark": "./benchmark", + "doc": "./docs", + "example": "./examples", + "lib": "./lib", + "test": "./test" + }, + "types": "./docs/types", + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "utilities", + "utils", + "generic", + "array", + "set", + "replace", + "put", + "update", + "mask", + "missing", + "na" + ] +} diff --git a/place/test/test.js b/place/test/test.js new file mode 100644 index 00000000..e773d8ee --- /dev/null +++ b/place/test/test.js @@ -0,0 +1,696 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var Complex64Array = require( './../../complex64' ); +var Int32Array = require( './../../int32' ); +var BooleanArray = require( './../../bool' ); +var zeros = require( './../../zeros' ); +var toAccessorArray = require( './../../base/to-accessor-array' ); +var isAccessorArray = require( '@stdlib/assert/is-accessor-array' ); +var Complex64 = require( '@stdlib/complex/float32' ); +var realf = require( '@stdlib/complex/realf' ); +var imagf = require( '@stdlib/complex/imagf' ); +var isComplex64 = require( '@stdlib/assert/is-complex64' ); +var place = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof place, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function throws an error if provided a first argument which is not a collection', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + {}, + function noop() {} + ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( value, [], [] ); + }; + } +}); + +tape( 'the function throws an error if provided a first argument which is not a collection (options)', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + {}, + function noop() {} + ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( value, [], [], {} ); + }; + } +}); + +tape( 'the function throws an error if provided a second argument which is not a collection', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + {}, + function noop() {} + ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( [], value, [] ); + }; + } +}); + +tape( 'the function throws an error if provided a second argument which is not a collection (options)', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + {}, + function noop() {} + ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( [], value, [], {} ); + }; + } +}); + +tape( 'the function throws an error if provided a third argument which is not a collection', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + {}, + function noop() {} + ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( [], [], value ); + }; + } +}); + +tape( 'the function throws an error if provided a third argument which is not a collection (options)', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + {}, + function noop() {} + ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( [], [], value, {} ); + }; + } +}); + +tape( 'the function throws an error if provided an options argument which is not an object', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + [], + function noop() {} + ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( [], [], [], value ); + }; + } +}); + +tape( 'the function throws an error if provided a `mode` option which is not a valid mode', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + [], + {}, + function noop() {} + ]; + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( [], [], [], { + 'mode': value + }); + }; + } +}); + +tape( 'the function throws an error if provided a third argument which cannot be safely cast to the input array data type', function test( t ) { + var values; + var x; + var i; + + values = zeros( 5, 'float64' ); + + x = [ + zeros( 5, 'uint8' ), + zeros( 5, 'int8' ), + zeros( 5, 'int16' ), + zeros( 5, 'uint16' ) + ]; + for ( i = 0; i < x.length; i++ ) { + t.throws( badValue( x[ i ] ), TypeError, 'throws an error when provided ' + x[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + place( value, zeros( 5, 'generic' ), values ); + }; + } +}); + +tape( 'the function replaces elements in an array (generic)', function test( t ) { + var expected; + var actual; + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = [ 0, 1, 0, 1 ]; + actual = place( x, mask, [ 20, 40 ] ); + expected = [ 1, 20, 3, 40 ]; + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + actual = place( x, mask, [ 20, 30, 40, 50 ] ); + expected = [ 20, 30, 40, 50 ]; + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = [ 0, 0, 0, 0 ]; + actual = place( x, mask, [ 20, 30, 40, 50 ] ); + expected = [ 1, 2, 3, 4 ]; + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function replaces elements in an array (generic, broadcasting)', function test( t ) { + var expected; + var actual; + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = [ 0, 1, 0, 1 ]; + actual = place( x, mask, [ 20 ] ); + expected = [ 1, 20, 3, 20 ]; + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + actual = place( x, mask, [ 20 ] ); + expected = [ 20, 20, 20, 20 ]; + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = [ 0, 0, 0, 0 ]; + actual = place( x, mask, [ 20 ] ); + expected = [ 1, 2, 3, 4 ]; + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + actual = place( x, mask, [ 100, 200 ] ); + expected = [ 100, 200, 100, 200 ]; + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function replaces elements in an array (typed)', function test( t ) { + var expected; + var actual; + var mask; + var x; + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + mask = [ 0, 1, 0, 1 ]; + actual = place( x, mask, new Int32Array( [ 20, 40 ] ) ); + expected = new Int32Array( [ 1, 20, 3, 40 ] ); + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + mask = [ 1, 1, 1, 1 ]; + actual = place( x, mask, new Int32Array( [ 20, 30, 40, 50 ] ) ); + expected = new Int32Array( [ 20, 30, 40, 50 ] ); + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + mask = [ 0, 0, 0, 0 ]; + actual = place( x, mask, new Int32Array( [ 20, 30, 40, 50 ] ) ); + expected = new Int32Array( [ 1, 2, 3, 4 ] ); + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function replaces elements in an array (typed, broadcasting)', function test( t ) { + var expected; + var actual; + var mask; + var x; + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + mask = [ 0, 1, 0, 1 ]; + actual = place( x, mask, new Int32Array( [ 20 ] ) ); + expected = new Int32Array( [ 1, 20, 3, 20 ] ); + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + mask = [ 1, 1, 1, 1 ]; + actual = place( x, mask, new Int32Array( [ 20 ] ) ); + expected = new Int32Array( [ 20, 20, 20, 20 ] ); + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + mask = [ 0, 0, 0, 0 ]; + actual = place( x, mask, new Int32Array( [ 20 ] ) ); + expected = new Int32Array( [ 1, 2, 3, 4 ] ); + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + mask = [ 1, 1, 1, 1 ]; + actual = place( x, mask, new Int32Array( [ 100, 200 ] ) ); + expected = new Int32Array( [ 100, 200, 100, 200 ] ); + t.strictEqual( actual, x, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function replaces elements in an array (accessors)', function test( t ) { + var expected; + var actual; + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = toAccessorArray( [ 0, 1, 0, 1 ] ); + actual = place( toAccessorArray( x ), mask, toAccessorArray( [ 20, 40 ] ) ); + expected = [ 1, 20, 3, 40 ]; + t.strictEqual( isAccessorArray( actual ), true, 'returns expected value' ); + t.deepEqual( x, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = toAccessorArray( [ 1, 1, 1, 1 ] ); + actual = place( toAccessorArray( x ), mask, toAccessorArray( [ 20, 30, 40, 50 ] ) ); // eslint-disable-line max-len + expected = [ 20, 30, 40, 50 ]; + t.strictEqual( isAccessorArray( actual ), true, 'returns expected value' ); + t.deepEqual( x, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = toAccessorArray( [ 0, 0, 0, 0 ] ); + actual = place( toAccessorArray( x ), mask, [ 20, 30, 40, 50 ] ); + expected = [ 1, 2, 3, 4 ]; + t.strictEqual( isAccessorArray( actual ), true, 'returns expected value' ); + t.deepEqual( x, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function replaces elements in an array (accessors, broadcasting)', function test( t ) { + var expected; + var actual; + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = toAccessorArray( [ 0, 1, 0, 1 ] ); + actual = place( toAccessorArray( x ), mask, toAccessorArray( [ 20 ] ) ); + expected = [ 1, 20, 3, 20 ]; + t.strictEqual( isAccessorArray( actual ), true, 'returns expected value' ); + t.deepEqual( x, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = toAccessorArray( [ 1, 1, 1, 1 ] ); + actual = place( toAccessorArray( x ), mask, toAccessorArray( [ 20 ] ) ); + expected = [ 20, 20, 20, 20 ]; + t.strictEqual( isAccessorArray( actual ), true, 'returns expected value' ); + t.deepEqual( x, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = toAccessorArray( [ 0, 0, 0, 0 ] ); + actual = place( toAccessorArray( x ), mask, toAccessorArray( [ 20 ] ) ); + expected = [ 1, 2, 3, 4 ]; + t.strictEqual( isAccessorArray( actual ), true, 'returns expected value' ); + t.deepEqual( x, expected, 'returns expected value' ); + + x = [ 1, 2, 3, 4 ]; + mask = toAccessorArray( [ 1, 1, 1, 1 ] ); + actual = place( toAccessorArray( x ), mask, toAccessorArray( [ 100, 200 ] ) ); // eslint-disable-line max-len + expected = [ 100, 200, 100, 200 ]; + t.strictEqual( isAccessorArray( actual ), true, 'returns expected value' ); + t.deepEqual( x, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function replaces elements in an array (accessors, complex)', function test( t ) { + var expected; + var actual; + var values; + var mask; + var x; + var v; + var i; + + x = new Complex64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + mask = toAccessorArray( [ 0, 1, 0, 1 ] ); + values = new Complex64Array( [ 30.0, 40.0, 70.0, 80.0 ] ); + expected = [ + new Complex64( 1.0, 2.0 ), + new Complex64( 30.0, 40.0 ), + new Complex64( 5.0, 6.0 ), + new Complex64( 70.0, 80.0 ) + ]; + actual = place( x, mask, values ); + + t.strictEqual( actual, x, 'returns expected value' ); + for ( i = 0; i < mask.length; i++ ) { + v = actual.get( i ); + t.strictEqual( isComplex64( v ), true, 'returns expected value' ); + t.strictEqual( realf( v ), realf( expected[ i ] ), 'returns expected value' ); + t.strictEqual( imagf( v ), imagf( expected[ i ] ), 'returns expected value' ); + } + t.end(); +}); + +tape( 'the function replaces elements in an array (accessors, complex, broadcasting)', function test( t ) { + var expected; + var actual; + var values; + var mask; + var x; + var v; + var i; + + x = new Complex64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + mask = toAccessorArray( [ 0, 1, 0, 1 ] ); + values = new Complex64Array( [ 100.0, 200.0 ] ); + expected = [ + new Complex64( 1.0, 2.0 ), + new Complex64( 100.0, 200.0 ), + new Complex64( 5.0, 6.0 ), + new Complex64( 100.0, 200.0 ) + ]; + actual = place( x, mask, values ); + + t.strictEqual( actual, x, 'returns expected value' ); + for ( i = 0; i < mask.length; i++ ) { + v = actual.get( i ); + t.strictEqual( isComplex64( v ), true, 'returns expected value' ); + t.strictEqual( realf( v ), realf( expected[ i ] ), 'returns expected value' ); + t.strictEqual( imagf( v ), imagf( expected[ i ] ), 'returns expected value' ); + } + t.end(); +}); + +tape( 'the function replaces elements in an array (accessors, boolean)', function test( t ) { + var expected; + var actual; + var values; + var mask; + var x; + var v; + var i; + + x = new BooleanArray( [ true, false, false, true ] ); + mask = toAccessorArray( [ 0, 1, 0, 1 ] ); + values = new BooleanArray( [ true, false ] ); + expected = [ true, true, false, true ]; + actual = place( x, mask, values ); + + t.strictEqual( actual, x, 'returns expected value' ); + for ( i = 0; i < mask.length; i++ ) { + v = actual.get( i ); + t.strictEqual( v, expected[ i ], 'returns expected value' ); + } + t.end(); +}); + +tape( 'the function replaces elements in an array (accessors, boolean, broadcasting)', function test( t ) { + var expected; + var actual; + var values; + var mask; + var x; + var v; + var i; + + x = new BooleanArray( [ true, false, false, true ] ); + mask = toAccessorArray( [ 0, 1, 1, 0 ] ); + values = new BooleanArray( [ true ] ); + expected = [ true, true, true, true ]; + actual = place( x, mask, values ); + + t.strictEqual( actual, x, 'returns expected value' ); + for ( i = 0; i < mask.length; i++ ) { + v = actual.get( i ); + t.strictEqual( v, expected[ i ], 'returns expected value' ); + } + t.end(); +}); + +tape( 'when the "mode" is "strict", the function throws an error if provided insufficient values to satisfy the mask array', function test( t ) { + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + + t.throws( badValue, Error, 'throws an error' ); + t.end(); + + function badValue() { + place( x, mask, [ 200 ], { + 'mode': 'strict' + }); + } +}); + +tape( 'when the "mode" is "strict", the function throws an error if provided too many values to satisfy the mask array', function test( t ) { + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + + t.throws( badValue, Error, 'throws an error' ); + t.end(); + + function badValue() { + place( x, mask, [ 200, 300, 400, 500, 600 ], { + 'mode': 'strict' + }); + } +}); + +tape( 'when the "mode" is "non_strict", the function throws an error if provided insufficient values to satisfy the mask array', function test( t ) { + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + + t.throws( badValue, Error, 'throws an error' ); + t.end(); + + function badValue() { + place( x, mask, [ 200 ], { + 'mode': 'non_strict' + }); + } +}); + +tape( 'when the "mode" is "strict_broadcast", the function throws an error if provided a values array which is broadcast incompatible with the number of truthy values in a mask array (insufficient)', function test( t ) { + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + + t.throws( badValue, Error, 'throws an error' ); + t.end(); + + function badValue() { + place( x, mask, [ 200, 400 ], { + 'mode': 'strict_broadcast' + }); + } +}); + +tape( 'when the "mode" is "strict_broadcast", the function throws an error if provided a values array which is broadcast incompatible with the number of truthy values in a mask array (too many)', function test( t ) { + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + + t.throws( badValue, Error, 'throws an error' ); + t.end(); + + function badValue() { + place( x, mask, [ 200, 400, 500, 600, 700 ], { + 'mode': 'strict_broadcast' + }); + } +}); + +tape( 'when the "mode" is "broadcast", the function throws an error if provided a values array which is broadcast incompatible with the number of truthy values in a mask array', function test( t ) { + var mask; + var x; + + x = [ 1, 2, 3, 4 ]; + mask = [ 1, 1, 1, 1 ]; + + t.throws( badValue, Error, 'throws an error' ); + t.end(); + + function badValue() { + place( x, mask, [ 200, 400 ], { + 'mode': 'broadcast' + }); + } +}); diff --git a/place/test/test.validate.js b/place/test/test.validate.js new file mode 100644 index 00000000..50239210 --- /dev/null +++ b/place/test/test.validate.js @@ -0,0 +1,128 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var validate = require( './../lib/validate.js' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof validate, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function returns an error if not provided an options object', function test( t ) { + var values; + var err; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + [], + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + err = validate( {}, values[i] ); + t.strictEqual( err instanceof TypeError, true, 'returns an error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns an error if provided a `mode` option which is not a valid index mode', function test( t ) { + var values; + var err; + var i; + + values = [ + '5', + 5, + NaN, + true, + false, + null, + void 0, + [], + {}, + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + err = validate( {}, { + 'mode': values[i] + }); + t.strictEqual( err instanceof TypeError, true, 'returns an error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns `null` if all options are valid', function test( t ) { + var expected; + var options; + var opts; + var err; + + options = { + 'mode': 'strict' + }; + opts = {}; + + expected = { + 'mode': 'strict' + }; + + err = validate( opts, options ); + + t.strictEqual( err, null, 'returns expected value' ); + t.deepEqual( opts, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function ignores unrecognized options', function test( t ) { + var options; + var opts; + var err; + + options = { + 'beep': 'boop', + 'foo': 5, + 'bar': {} + }; + + opts = {}; + + err = validate( opts, options ); + + t.strictEqual( err, null, 'returns expected value' ); + t.deepEqual( opts, {}, 'returns expected value' ); + + t.end(); +});