diff --git a/base/bifurcate-entries-by/README.md b/base/bifurcate-entries-by/README.md index ef650f9b..fd538b90 100644 --- a/base/bifurcate-entries-by/README.md +++ b/base/bifurcate-entries-by/README.md @@ -103,7 +103,7 @@ var cnt = context.count; ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var bifurcateEntriesBy = require( '@stdlib/array/base/bifurcate-entries-by' ); function predicate( v ) { diff --git a/base/bifurcate-entries-by/examples/index.js b/base/bifurcate-entries-by/examples/index.js index 29171fdc..5d8648ce 100644 --- a/base/bifurcate-entries-by/examples/index.js +++ b/base/bifurcate-entries-by/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var bifurcateEntriesBy = require( './../lib' ); function predicate( v ) { diff --git a/base/bifurcate-entries/README.md b/base/bifurcate-entries/README.md index 94c2b16f..a97d93dc 100644 --- a/base/bifurcate-entries/README.md +++ b/base/bifurcate-entries/README.md @@ -78,7 +78,7 @@ var out = bifurcateEntries( x, filter ); ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var bifurcateEntries = require( '@stdlib/array/base/bifurcate-entries' ); // Define an initial array of values: diff --git a/base/bifurcate-entries/examples/index.js b/base/bifurcate-entries/examples/index.js index d0fc5610..05a08df1 100644 --- a/base/bifurcate-entries/examples/index.js +++ b/base/bifurcate-entries/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var bifurcateEntries = require( './../lib' ); // Define an initial array of values: diff --git a/base/bifurcate-indices-by/README.md b/base/bifurcate-indices-by/README.md index 09951f88..e4433eef 100644 --- a/base/bifurcate-indices-by/README.md +++ b/base/bifurcate-indices-by/README.md @@ -103,7 +103,7 @@ var cnt = context.count; ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var bifurcateIndicesBy = require( '@stdlib/array/base/bifurcate-indices-by' ); function predicate( v ) { diff --git a/base/bifurcate-indices-by/examples/index.js b/base/bifurcate-indices-by/examples/index.js index 32fee448..41db341b 100644 --- a/base/bifurcate-indices-by/examples/index.js +++ b/base/bifurcate-indices-by/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var bifurcateIndicesBy = require( './../lib' ); function predicate( v ) { diff --git a/base/bifurcate-indices/README.md b/base/bifurcate-indices/README.md index 512034d4..e1f85ad2 100644 --- a/base/bifurcate-indices/README.md +++ b/base/bifurcate-indices/README.md @@ -78,7 +78,7 @@ var out = bifurcateIndices( x, filter ); ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var bifurcateIndices = require( '@stdlib/array/base/bifurcate-indices' ); // Define an initial array of values: diff --git a/base/bifurcate-indices/examples/index.js b/base/bifurcate-indices/examples/index.js index f6d58973..acc01b9c 100644 --- a/base/bifurcate-indices/examples/index.js +++ b/base/bifurcate-indices/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var bifurcateIndices = require( './../lib' ); // Define an initial array of values: diff --git a/base/bifurcate-values-by/README.md b/base/bifurcate-values-by/README.md index f8761152..86ed195c 100644 --- a/base/bifurcate-values-by/README.md +++ b/base/bifurcate-values-by/README.md @@ -103,7 +103,7 @@ var cnt = context.count; ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var bifurcateValuesBy = require( '@stdlib/array/base/bifurcate-values-by' ); function predicate( v ) { diff --git a/base/bifurcate-values-by/examples/index.js b/base/bifurcate-values-by/examples/index.js index 756cd64a..00eefb73 100644 --- a/base/bifurcate-values-by/examples/index.js +++ b/base/bifurcate-values-by/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var bifurcateValuesBy = require( './../lib' ); function predicate( v ) { diff --git a/base/bifurcate-values/README.md b/base/bifurcate-values/README.md index 04537763..c56c4f01 100644 --- a/base/bifurcate-values/README.md +++ b/base/bifurcate-values/README.md @@ -78,7 +78,7 @@ var out = bifurcateValues( x, filter ); ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var bifurcateValues = require( '@stdlib/array/base/bifurcate-values' ); // Define an initial array of values: diff --git a/base/bifurcate-values/examples/index.js b/base/bifurcate-values/examples/index.js index 5530fffc..5bcfddf1 100644 --- a/base/bifurcate-values/examples/index.js +++ b/base/bifurcate-values/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var bifurcateValues = require( './../lib' ); // Define an initial array of values: diff --git a/base/fancy-slice-assign/lib/main.js b/base/fancy-slice-assign/lib/main.js index 2eb0d0d7..96a4709a 100644 --- a/base/fancy-slice-assign/lib/main.js +++ b/base/fancy-slice-assign/lib/main.js @@ -21,9 +21,12 @@ // 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 normalizeSlice = require( '@stdlib/slice/base/normalize-slice' ); var sliceLength = require( '@stdlib/slice/base/length' ); var dtype = require( './../../../dtype' ); +var convert = require( './../../../convert' ); var gcopy = require( '@stdlib/blas/base/gcopy' ).ndarray; var format = require( '@stdlib/string/format' ); @@ -92,6 +95,10 @@ function sliceAssign( x, y, s, strict ) { if ( !isMostlySafeCast( xdt, ydt ) ) { throw new TypeError( format( 'invalid argument. Input array values cannot be safely cast to the output array data type. Data types: [%s, %s].', xdt, ydt ) ); } + // 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( ydt ) && isRealDataType( xdt ) ) { + x = convert( x, ydt ); + } // Normalize the slice object base on the output array length: ns = normalizeSlice( s, ylen, true ); diff --git a/base/fancy-slice-assign/test/test.js b/base/fancy-slice-assign/test/test.js index a7c0b3b1..e3c1fc29 100644 --- a/base/fancy-slice-assign/test/test.js +++ b/base/fancy-slice-assign/test/test.js @@ -21,8 +21,13 @@ // MODULES // var tape = require( 'tape' ); +var isSameComplex128Array = require( '@stdlib/assert/is-same-complex128array' ); +var isSameComplex64Array = require( '@stdlib/assert/is-same-complex64array' ); var AccessorArray = require( './../../../base/accessor' ); var Float64Array = require( './../../../float64' ); +var Float32Array = require( './../../../float32' ); +var Complex128Array = require( './../../../complex128' ); +var Complex64Array = require( './../../../complex64' ); var Int32Array = require( './../../../int32' ); var zeros = require( './../../../zeros' ); var Slice = require( '@stdlib/slice/ctor' ); @@ -261,15 +266,15 @@ tape( 'the function performs slice assignment with support for broadcasting (flo t.deepEqual( actual, expected, 'returns expected value' ); x = new Float64Array( [ 1.0, 2.0, 3.0 ] ); - y = zeros( 3, 'float64' ); + y = zeros( 3, 'float32' ); // downcast - expected = new Float64Array( [ 3.0, 2.0, 1.0 ] ); + expected = new Float32Array( [ 3.0, 2.0, 1.0 ] ); actual = sliceAssign( x, y, new Slice( null, null, -1 ), false ); t.strictEqual( actual, y, 'returns expected value' ); t.deepEqual( actual, expected, 'returns expected value' ); - x = new Float64Array( [ 1, 3 ] ); + x = new Float64Array( [ 1.0, 3.0 ] ); y = zeros( 3, 'float64' ); expected = new Float64Array( [ 3.0, 0.0, 1.0 ] ); @@ -278,16 +283,16 @@ tape( 'the function performs slice assignment with support for broadcasting (flo t.strictEqual( actual, y, 'returns expected value' ); t.deepEqual( actual, expected, 'returns expected value' ); - x = new Float64Array( [ 1, 3 ] ); - y = zeros( 3, 'float64' ); + x = new Float64Array( [ 1.0, 3.0 ] ); + y = zeros( 3, 'float32' ); // downcast - expected = new Float64Array( [ 1.0, 0.0, 3.0 ] ); + expected = new Float32Array( [ 1.0, 0.0, 3.0 ] ); actual = sliceAssign( x, y, new Slice( null, null, 2 ), false ); t.strictEqual( actual, y, 'returns expected value' ); t.deepEqual( actual, expected, 'returns expected value' ); - x = new Float64Array( [ 2, 3 ] ); + x = new Float64Array( [ 2.0, 3.0 ] ); y = zeros( 3, 'float64' ); expected = new Float64Array( [ 0.0, 2.0, 3.0 ] ); @@ -296,7 +301,7 @@ tape( 'the function performs slice assignment with support for broadcasting (flo t.strictEqual( actual, y, 'returns expected value' ); t.deepEqual( actual, expected, 'returns expected value' ); - x = new Float64Array( [ 2 ] ); + x = new Float64Array( [ 2.0 ] ); y = zeros( 3, 'float64' ); expected = new Float64Array( [ 0.0, 2.0, 0.0 ] ); @@ -305,7 +310,7 @@ tape( 'the function performs slice assignment with support for broadcasting (flo t.strictEqual( actual, y, 'returns expected value' ); t.deepEqual( actual, expected, 'returns expected value' ); - x = new Float64Array( [ 1 ] ); + x = new Float64Array( [ 1.0 ] ); y = zeros( 3, 'float64' ); expected = new Float64Array( [ 0.0, 0.0, 0.0 ] ); @@ -315,7 +320,7 @@ tape( 'the function performs slice assignment with support for broadcasting (flo t.deepEqual( actual, expected, 'returns expected value' ); // Broadcasting: - x = new Float64Array( [ 1 ] ); + x = new Float64Array( [ 1.0 ] ); y = zeros( 3, 'float64' ); expected = new Float64Array( [ 1.0, 1.0, 1.0 ] ); @@ -324,7 +329,7 @@ tape( 'the function performs slice assignment with support for broadcasting (flo t.strictEqual( actual, y, 'returns expected value' ); t.deepEqual( actual, expected, 'returns expected value' ); - x = new Float64Array( [ 3 ] ); + x = new Float64Array( [ 3.0 ] ); y = zeros( 3, 'float64' ); expected = new Float64Array( [ 3.0, 0.0, 3.0 ] ); @@ -333,6 +338,107 @@ tape( 'the function performs slice assignment with support for broadcasting (flo t.strictEqual( actual, y, 'returns expected value' ); t.deepEqual( actual, expected, 'returns expected value' ); + // Upcasting: + x = new Float64Array( [ 1.0, 2.0, 3.0 ] ); + y = zeros( 3, 'complex128' ); + + expected = new Complex128Array( [ 1.0, 0.0, 2.0, 0.0, 3.0, 0.0 ] ); + actual = sliceAssign( x, y, new Slice(), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function performs slice assignment with support for broadcasting (complex128)', function test( t ) { + var expected; + var actual; + var x; + var y; + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); + y = zeros( 3, 'complex128' ); + + expected = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); + actual = sliceAssign( x, y, new Slice(), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); + y = zeros( 3, 'complex128' ); + + expected = new Complex128Array( [ 5.0, 6.0, 3.0, 4.0, 1.0, 2.0 ] ); + actual = sliceAssign( x, y, new Slice( null, null, -1 ), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + y = zeros( 3, 'complex64' ); // downcast + + expected = new Complex64Array( [ 3.0, 4.0, 0.0, 0.0, 1.0, 2.0 ] ); + actual = sliceAssign( x, y, new Slice( null, null, -2 ), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex64Array( actual, expected ), true, 'returns expected value' ); + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + y = zeros( 3, 'complex128' ); + + expected = new Complex128Array( [ 1.0, 2.0, 0.0, 0.0, 3.0, 4.0 ] ); + actual = sliceAssign( x, y, new Slice( null, null, 2 ), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + y = zeros( 3, 'complex128' ); + + expected = new Complex128Array( [ 0.0, 0.0, 1.0, 2.0, 3.0, 4.0 ] ); + actual = sliceAssign( x, y, new Slice( 1, null ), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + x = new Complex128Array( [ 1.0, 2.0 ] ); + y = zeros( 3, 'complex64' ); // downcast + + expected = new Complex64Array( [ 0.0, 0.0, 1.0, 2.0, 0.0, 0.0 ] ); + actual = sliceAssign( x, y, new Slice( 1, y.length-1 ), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex64Array( actual, expected ), true, 'returns expected value' ); + + x = new Complex128Array( [ 1.0, 2.0 ] ); + y = zeros( 3, 'complex128' ); + + expected = new Complex128Array( [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] ); + actual = sliceAssign( x, y, new Slice( 1, 1 ), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + // Broadcasting: + x = new Complex128Array( [ 1.0, 2.0 ] ); + y = zeros( 3, 'complex128' ); + + expected = new Complex128Array( [ 1.0, 2.0, 1.0, 2.0, 1.0, 2.0 ] ); + actual = sliceAssign( x, y, new Slice(), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + x = new Complex128Array( [ 3.0, 4.0 ] ); + y = zeros( 3, 'complex64' ); // downcast + + expected = new Complex64Array( [ 3.0, 4.0, 0.0, 0.0, 3.0, 4.0 ] ); + actual = sliceAssign( x, y, new Slice( null, null, 2 ), false ); + + t.strictEqual( actual, y, 'returns expected value' ); + t.strictEqual( isSameComplex64Array( actual, expected ), true, 'returns expected value' ); + t.end(); }); diff --git a/base/group-entries-by/README.md b/base/group-entries-by/README.md index 32762748..3ea2b7e1 100644 --- a/base/group-entries-by/README.md +++ b/base/group-entries-by/README.md @@ -119,7 +119,7 @@ var cnt = context.count; ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var groupEntriesBy = require( '@stdlib/array/base/group-entries-by' ); function indicator( v ) { diff --git a/base/group-entries-by/examples/index.js b/base/group-entries-by/examples/index.js index c60ec912..a81cfeca 100644 --- a/base/group-entries-by/examples/index.js +++ b/base/group-entries-by/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var groupEntriesBy = require( './../lib' ); function indicator( v ) { diff --git a/base/group-entries/README.md b/base/group-entries/README.md index 79c45b83..a96910f4 100644 --- a/base/group-entries/README.md +++ b/base/group-entries/README.md @@ -88,7 +88,7 @@ var out = groupEntries( x, groups ); ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var groupEntries = require( '@stdlib/array/base/group-entries' ); // Define an initial array of values: diff --git a/base/group-entries/examples/index.js b/base/group-entries/examples/index.js index 8aa03bd0..652ab459 100644 --- a/base/group-entries/examples/index.js +++ b/base/group-entries/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var groupEntries = require( './../lib' ); // Define an initial array of values: diff --git a/base/group-indices-by/README.md b/base/group-indices-by/README.md index 0ae6809b..2cbad4ce 100644 --- a/base/group-indices-by/README.md +++ b/base/group-indices-by/README.md @@ -119,7 +119,7 @@ var cnt = context.count; ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var groupIndicesBy = require( '@stdlib/array/base/group-indices-by' ); function indicator( v ) { diff --git a/base/group-indices-by/examples/index.js b/base/group-indices-by/examples/index.js index e5d405b8..ab07de39 100644 --- a/base/group-indices-by/examples/index.js +++ b/base/group-indices-by/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var groupIndicesBy = require( './../lib' ); function indicator( v ) { diff --git a/base/group-indices/README.md b/base/group-indices/README.md index 992dcbad..18703746 100644 --- a/base/group-indices/README.md +++ b/base/group-indices/README.md @@ -88,7 +88,7 @@ var out = groupIndices( x, groups ); ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var groupIndices = require( '@stdlib/array/base/group-indices' ); // Define an initial array of values: diff --git a/base/group-indices/examples/index.js b/base/group-indices/examples/index.js index 95cf3f18..37217361 100644 --- a/base/group-indices/examples/index.js +++ b/base/group-indices/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var groupIndices = require( './../lib' ); // Define an initial array of values: diff --git a/base/group-values-by/README.md b/base/group-values-by/README.md index 124428bb..ee14a57b 100644 --- a/base/group-values-by/README.md +++ b/base/group-values-by/README.md @@ -119,7 +119,7 @@ var cnt = context.count; ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var groupValuesBy = require( '@stdlib/array/base/group-values-by' ); function indicator( v ) { diff --git a/base/group-values-by/examples/index.js b/base/group-values-by/examples/index.js index 8a0af9c0..56ec38e7 100644 --- a/base/group-values-by/examples/index.js +++ b/base/group-values-by/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var groupValuesBy = require( './../lib' ); function indicator( v ) { diff --git a/base/group-values/README.md b/base/group-values/README.md index 7d250335..bd228e1a 100644 --- a/base/group-values/README.md +++ b/base/group-values/README.md @@ -88,7 +88,7 @@ var out = groupValues( x, groups ); ```javascript var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( '@stdlib/array/base/take' ); +var take = require( '@stdlib/array/base/take-indexed' ); var groupValues = require( '@stdlib/array/base/group-values' ); // Define an initial array of values: diff --git a/base/group-values/examples/index.js b/base/group-values/examples/index.js index 4e97ac48..2775b3e1 100644 --- a/base/group-values/examples/index.js +++ b/base/group-values/examples/index.js @@ -19,7 +19,7 @@ 'use strict'; var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); -var take = require( './../../../base/take' ); +var take = require( './../../../base/take-indexed' ); var groupValues = require( './../lib' ); // Define an initial array of values: diff --git a/base/take/README.md b/base/take/README.md index 525fb8e5..89394087 100644 --- a/base/take/README.md +++ b/base/take/README.md @@ -30,36 +30,64 @@ limitations under the License. var take = require( '@stdlib/array/base/take' ); ``` -#### take( x, indices ) +#### take( x, indices, mode ) Takes elements from an array. ```javascript var x = [ 1, 2, 3, 4 ]; -var y = take( x, [ 1, 3 ] ); +var y = take( x, [ 1, 3 ], 'throw' ); // returns [ 2, 4 ] ``` +The function supports the following parameters: + +- **x**: input array. +- **indices**: list of indices. +- **mode**: index [mode][@stdlib/ndarray/base/ind]. + If `indices` is an empty array, the function returns an empty array. ```javascript var x = [ 1, 2, 3, 4 ]; -var y = take( x, [] ); +var y = take( x, [], 'throw' ); // returns [] ``` +#### take.assign( x, indices, mode, out, stride, offset ) + +Takes elements from an array and assigns the values to elements in a provided output array. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var out = [ 0, 0, 0, 0, 0, 0 ]; +var indices = [ 0, 0, 1, 1, 3, 3 ]; + +var arr = take.assign( x, indices, 'throw', out, -1, out.length-1 ); +// returns [ 4, 4, 2, 2, 1, 1 ] + +var bool = ( arr === out ); +// returns true +``` + +The function supports the following parameters: + +- **x**: input array. +- **indices**: list of indices. +- **mode**: index [mode][@stdlib/ndarray/base/ind]. +- **out**: output array. +- **stride**: output array stride. +- **offset**: output array offset. +
-## Notes - -- The function does **not** perform bounds checking. If an index is less than zero or greater than the maximum index of `x`, the value of the corresponding element in the output array is undefined. -
@@ -84,7 +112,7 @@ var N = discreteUniform( 5, 15 ); var indices = filledBy( N, discreteUniform.factory( 0, x.length-1 ) ); // Take a random sample of elements from `x`: -var y = take( x, indices ); +var y = take( x, indices, 'throw' ); console.log( x ); console.log( indices ); @@ -107,6 +135,8 @@ console.log( y ); diff --git a/base/take/benchmark/benchmark.assign.length.js b/base/take/benchmark/benchmark.assign.length.js new file mode 100644 index 00000000..330c1c46 --- /dev/null +++ b/base/take/benchmark/benchmark.assign.length.js @@ -0,0 +1,106 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2022 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 zeros = require( './../../../zeros' ); +var isArray = require( '@stdlib/assert/is-array' ); +var pkg = require( './../package.json' ).name; +var take = require( './../lib' ); + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} len - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( len ) { + var out; + var idx; + + idx = discreteUniform( len, 0, 3, { + 'dtype': 'generic' + }); + out = zeros( len, 'generic' ); + + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var x; + var v; + var i; + + x = [ 1, 2, 3, 4 ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = take.assign( x, idx, 'throw', out, 1, 0 ); + 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+':assign:len='+len, f ); + } +} + +main(); diff --git a/base/take/benchmark/benchmark.js b/base/take/benchmark/benchmark.js index 51ae6f5e..fdd872ce 100644 --- a/base/take/benchmark/benchmark.js +++ b/base/take/benchmark/benchmark.js @@ -38,7 +38,7 @@ bench( pkg+'::copy:len=100', function benchmark( b ) { b.tic(); for ( i = 0; i < b.iterations; i++ ) { - v = take( x, x ); + v = take( x, x, 'throw' ); if ( typeof v !== 'object' ) { b.fail( 'should return an array' ); } diff --git a/base/take/benchmark/benchmark.length.js b/base/take/benchmark/benchmark.length.js index 01697bdc..1ac63bf0 100644 --- a/base/take/benchmark/benchmark.length.js +++ b/base/take/benchmark/benchmark.length.js @@ -22,8 +22,7 @@ var bench = require( '@stdlib/bench' ); var pow = require( '@stdlib/math/base/special/pow' ); -var filledBy = require( './../../../base/filled-by' ); -var discreteUniform = require( '@stdlib/random/base/discrete-uniform' ).factory; +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); var isArray = require( '@stdlib/assert/is-array' ); var pkg = require( './../package.json' ).name; var take = require( './../lib' ); @@ -39,7 +38,7 @@ var take = require( './../lib' ); * @returns {Function} benchmark function */ function createBenchmark( len ) { - var idx = filledBy( len, discreteUniform( 0, 3 ) ); + var idx = discreteUniform( len, 0, 3 ); return benchmark; /** @@ -57,7 +56,7 @@ function createBenchmark( len ) { b.tic(); for ( i = 0; i < b.iterations; i++ ) { - v = take( x, idx ); + v = take( x, idx, 'throw' ); if ( typeof v !== 'object' ) { b.fail( 'should return an array' ); } diff --git a/base/take/docs/repl.txt b/base/take/docs/repl.txt index bedbcf02..429279ba 100644 --- a/base/take/docs/repl.txt +++ b/base/take/docs/repl.txt @@ -1,13 +1,9 @@ -{{alias}}( x, indices ) +{{alias}}( x, indices, mode ) Takes elements from an array. If `indices` is an empty array, the function returns an empty array. - The function does *not* perform bounds checking. If an index is less than - zero or greater than the maximum index of `x`, the value of the - corresponding element in the output array is undefined. - Parameters ---------- x: ArrayLikeObject @@ -16,6 +12,15 @@ indices: ArrayLikeObject List of element indices. + mode: string + Specifies how to handle an index outside the interval [0, max], where + `max` is the maximum possible array index. If equal to 'throw', the + function throws an error. If equal to 'normalize', the function throws + an error if provided an out-of-bounds normalized index. If equal to + 'wrap', the function wraps around an index using modulo arithmetic. If + equal to 'clamp', the function sets an index to either 0 (minimum index) + or the maximum index. + Returns ------- out: Array @@ -24,9 +29,54 @@ Examples -------- > var x = [ 1, 2, 3, 4 ]; - > var y = {{alias}}( x, [ 1, 3 ] ) + > var y = {{alias}}( x, [ 1, 3 ], 'throw' ) [ 2, 4 ] + +{{alias}}.assign( x, indices, mode, out, stride, offset ) + Takes elements from an array and assigns the values to elements in a + provided output array. + + Parameters + ---------- + x: ArrayLikeObject + Input array. + + indices: ArrayLikeObject + List of element indices. + + mode: string + Specifies how to handle an index outside the interval [0, max], where + `max` is the maximum possible array index. If equal to 'throw', the + function throws an error. If equal to 'normalize', the function throws + an error if provided an out-of-bounds normalized index. If equal to + 'wrap', the function wraps around an index using modulo arithmetic. If + equal to 'clamp', the function sets an index to either 0 (minimum index) + or the maximum index. + + out: ArrayLikeObject + Output array. + + stride: integer + Output array stride. + + offset: integer + Output array offset. + + Returns + ------- + out: ArrayLikeObject + Output array. + + Examples + -------- + > var x = [ 1, 2, 3, 4 ]; + > var out = [ 0, 0, 0, 0 ]; + > var arr = {{alias}}.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ) + [ 2, 0, 4, 0 ] + > var bool = ( arr === out ) + true + See Also -------- diff --git a/base/take/docs/types/index.d.ts b/base/take/docs/types/index.d.ts index 04c74e02..06d7720b 100644 --- a/base/take/docs/types/index.d.ts +++ b/base/take/docs/types/index.d.ts @@ -20,26 +20,408 @@ /// -import { Collection } from '@stdlib/types/array'; +import { Collection, AccessorArrayLike, Complex128Array, Complex64Array } from '@stdlib/types/array'; +import { Mode } from '@stdlib/types/ndarray'; /** -* Takes element from an array. -* -* ## Notes -* -* - The function does **not** perform bounds checking. If an index is less than zero or greater than the maximum index of `x`, the value of the corresponding element in the output array is undefined. +* Index array. +*/ +type IndexArray = Collection | AccessorArrayLike; + +/** +* Interface describing `take`. +*/ +interface Take { + /** + * Takes elements from an array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @returns output array + * + * @example + * var x = [ 1, 2, 3, 4 ]; + * + * var y = take( x, [ 1, 3 ], 'throw' ); + * // returns [ 2, 4 ] + */ + ( x: Collection, indices: IndexArray, mode: Mode ): Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Float64Array = require( './../../../../float64' ); + * + * var x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + * var out = new Float64Array( [ 0.0, 0.0, 0.0, 0.0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2.0, 0.0, 4.0, 0.0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Float64Array, stride: number, offset: number ): Float64Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Float32Array = require( './../../../../float32' ); + * + * var x = new Float32Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + * var out = new Float32Array( [ 0.0, 0.0, 0.0, 0.0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2.0, 0.0, 4.0, 0.0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Float32Array, stride: number, offset: number ): Float32Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Complex128Array = require( './../../../../float64' ); + * + * var x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + * var out = new Complex128Array( [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 3.0, 4.0, 0.0, 0.0, 7.0, 8.0, 0.0, 0.0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Complex128Array, stride: number, offset: number ): Complex128Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Complex64Array = require( './../../../../float64' ); + * + * var x = new Complex64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + * var out = new Complex64Array( [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 3.0, 4.0, 0.0, 0.0, 7.0, 8.0, 0.0, 0.0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Complex64Array, stride: number, offset: number ): Complex64Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Int32Array = require( './../../../../int32' ); + * + * var x = new Int32Array( [ 1, 2, 3, 4 ] ); + * var out = new Int32Array( [ 0, 0, 0, 0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Int32Array, stride: number, offset: number ): Int32Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Int16Array = require( './../../../../int16' ); + * + * var x = new Int16Array( [ 1, 2, 3, 4 ] ); + * var out = new Int16Array( [ 0, 0, 0, 0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Int16Array, stride: number, offset: number ): Int16Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Int8Array = require( './../../../../int8' ); + * + * var x = new Int8Array( [ 1, 2, 3, 4 ] ); + * var out = new Int8Array( [ 0, 0, 0, 0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Int8Array, stride: number, offset: number ): Int8Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Uint32Array = require( './../../../../uint32' ); + * + * var x = new Uint32Array( [ 1, 2, 3, 4 ] ); + * var out = new Uint32Array( [ 0, 0, 0, 0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Uint32Array, stride: number, offset: number ): Uint32Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Uint16Array = require( './../../../../uint16' ); + * + * var x = new Uint16Array( [ 1, 2, 3, 4 ] ); + * var out = new Uint16Array( [ 0, 0, 0, 0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Uint16Array, stride: number, offset: number ): Uint16Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Uint8Array = require( './../../../../uint8' ); + * + * var x = new Uint8Array( [ 1, 2, 3, 4 ] ); + * var out = new Uint8Array( [ 0, 0, 0, 0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Uint8Array, stride: number, offset: number ): Uint8Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var Uint8ClampedArray = require( './../../../../uint8c' ); + * + * var x = new Uint8ClampedArray( [ 1, 2, 3, 4 ] ); + * var out = new Uint8ClampedArray( [ 0, 0, 0, 0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Uint8ClampedArray, stride: number, offset: number ): Uint8ClampedArray; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var x = [ 1, 2, 3, 4 ]; + * var out = [ 0, 0, 0, 0 ]; + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Array, stride: number, offset: number ): Array; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var toAccessorArray = require( './../../../../base/to-accessor-array' ); + * + * var x = [ 1, 2, 3, 4 ]; + * var out = toAccessorArray( [ 0, 0, 0, 0 ] ); + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * + * var bool = ( arr === out ); + * // returns true + * + * var v = arr.get( 0 ); + * // returns 2 + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: AccessorArrayLike, stride: number, offset: number ): AccessorArrayLike; + + /** + * Takes elements from an array and assigns the value to elements in a provided output array. + * + * @param x - input array + * @param indices - list of element indices + * @param mode - index mode + * @param out - output array + * @param stride - output array stride + * @param offset - output array offset + * @returns output array + * + * @example + * var x = [ 1, 2, 3, 4 ]; + * var out = [ 0, 0, 0, 0 ]; + * + * var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); + * // returns [ 2, 0, 4, 0 ] + * + * var bool = ( arr === out ); + * // returns true + */ + assign( x: Collection | AccessorArrayLike, indices: IndexArray, mode: Mode, out: Collection, stride: number, offset: number ): Collection; +} + +/** +* Takes elements from an array. * * @param x - input array * @param indices - list of element indices +* @param mode - index mode * @returns output array * * @example * var x = [ 1, 2, 3, 4 ]; * -* var y = take( x, [ 1, 3 ] ); +* var y = take( x, [ 1, 3 ], 'throw' ); * // returns [ 2, 4 ] +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* var out = [ 0, 0, 0, 0 ]; +* +* var arr = take.assign( x, [ 1, 3 ], 'throw', out, 2, 0 ); +* // returns [ 2, 0, 4, 0 ] +* +* var bool = ( arr === out ); +* // returns true */ -declare function take( x: Collection, indices: Collection ): Array; +declare var take: Take; // EXPORTS // diff --git a/base/take/docs/types/test.ts b/base/take/docs/types/test.ts index f9a84493..5ff071ae 100644 --- a/base/take/docs/types/test.ts +++ b/base/take/docs/types/test.ts @@ -16,6 +16,8 @@ * limitations under the License. */ +import Complex128Array = require( './../../../../complex128' ); +import Complex64Array = require( './../../../../complex64' ); import take = require( './index' ); @@ -23,35 +25,156 @@ import take = require( './index' ); // The function returns an array... { - take( [ 1, 2, 3, 4 ], [ 1, 3 ] ); // $ExpectType number[] - take( [ 1, 2, 3, 4 ], [ 1, 3 ] ); // $ExpectType any[] - take( [ 1, 2, 3, 4 ], [ 1, 3 ] ); // $ExpectType number[] - take( [ '1', '2', '3', '4' ], [ 1, 3 ] ); // $ExpectType string[] + take( [ 1, 2, 3, 4 ], [ 1, 3 ], 'throw' ); // $ExpectType number[] + take( [ 1, 2, 3, 4 ], [ 1, 3 ], 'normalize' ); // $ExpectType any[] + take( [ 1, 2, 3, 4 ], [ 1, 3 ], 'clamp' ); // $ExpectType number[] + take( [ '1', '2', '3', '4' ], [ 1, 3 ], 'wrap' ); // $ExpectType string[] } // The compiler throws an error if the function is provided a first argument which is not an array-like object... { - take( 1, [ 1, 3 ] ); // $ExpectError - take( true, [ 1, 3 ] ); // $ExpectError - take( false, [ 1, 3 ] ); // $ExpectError - take( null, [ 1, 3 ] ); // $ExpectError - take( void 0, [ 1, 3 ] ); // $ExpectError - take( {}, [ 1, 3 ] ); // $ExpectError + take( 1, [ 1, 3 ], 'throw' ); // $ExpectError + take( true, [ 1, 3 ], 'throw' ); // $ExpectError + take( false, [ 1, 3 ], 'throw' ); // $ExpectError + take( null, [ 1, 3 ], 'throw' ); // $ExpectError + take( void 0, [ 1, 3 ], 'throw' ); // $ExpectError + take( {}, [ 1, 3 ], 'throw' ); // $ExpectError } // The compiler throws an error if the function is provided a second argument which is not an array-like object containing numbers... { - take( [], 1 ); // $ExpectError - take( [], true ); // $ExpectError - take( [], false ); // $ExpectError - take( [], null ); // $ExpectError - take( [], void 0 ); // $ExpectError - take( [], {} ); // $ExpectError + take( [], 1, 'throw' ); // $ExpectError + take( [], true, 'throw' ); // $ExpectError + take( [], false, 'throw' ); // $ExpectError + take( [], null, 'throw' ); // $ExpectError + take( [], void 0, 'throw' ); // $ExpectError + take( [], {}, 'throw' ); // $ExpectError +} + +// The compiler throws an error if the function is provided a third argument which is not a valid index mode... +{ + take( [], [ 1, 3 ], '1' ); // $ExpectError + take( [], [ 1, 3 ], 1 ); // $ExpectError + take( [], [ 1, 3 ], true ); // $ExpectError + take( [], [ 1, 3 ], false ); // $ExpectError + take( [], [ 1, 3 ], null ); // $ExpectError + take( [], [ 1, 3 ], void 0 ); // $ExpectError + take( [], [ 1, 3 ], {} ); // $ExpectError + take( [], [ 1, 3 ], [] ); // $ExpectError + take( [], [ 1, 3 ], ( x: number ): number => x ); // $ExpectError } // The compiler throws an error if the function is provided an unsupported number of arguments... { take(); // $ExpectError take( [] ); // $ExpectError - take( [], [], [] ); // $ExpectError + take( [], [] ); // $ExpectError + take( [], [], 'throw', {} ); // $ExpectError +} + +// Attached to the main export is an `assign` method which returns a collection... +{ + const x = [ 1, 2, 3, 4 ]; + const y = new Complex128Array( 4 ); + + take.assign( x, [ 1, 3 ], 'throw', [ 0, 0, 0, 0 ], 1, 0 ); // $ExpectType number[] + take.assign( x, [ 1, 3 ], 'throw', new Float64Array( 4 ), 1, 0 ); // $ExpectType Float64Array + take.assign( x, [ 1, 3 ], 'throw', new Float32Array( 4 ), 1, 0 ); // $ExpectType Float32Array + take.assign( x, [ 1, 3 ], 'throw', new Int32Array( 4 ), 1, 0 ); // $ExpectType Int32Array + take.assign( x, [ 1, 3 ], 'throw', new Int16Array( 4 ), 1, 0 ); // $ExpectType Int16Array + take.assign( x, [ 1, 3 ], 'throw', new Int8Array( 4 ), 1, 0 ); // $ExpectType Int8Array + take.assign( x, [ 1, 3 ], 'throw', new Uint32Array( 4 ), 1, 0 ); // $ExpectType Uint32Array + take.assign( x, [ 1, 3 ], 'throw', new Uint16Array( 4 ), 1, 0 ); // $ExpectType Uint16Array + take.assign( x, [ 1, 3 ], 'throw', new Uint8Array( 4 ), 1, 0 ); // $ExpectType Uint8Array + take.assign( x, [ 1, 3 ], 'throw', new Uint8ClampedArray( 4 ), 1, 0 ); // $ExpectType Uint8ClampedArray + take.assign( y, [ 1, 3 ], 'throw', new Complex128Array( 4 ), 1, 0 ); // $ExpectType Complex128Array + take.assign( y, [ 1, 3 ], 'throw', new Complex64Array( 4 ), 1, 0 ); // $ExpectType Complex64Array +} + +// The compiler throws an error if the `assign` method is provided a first argument which is not an array-like object... +{ + const out = [ 0, 0, 0, 0 ]; + + take.assign( 1, [ 1, 3 ], 'throw', out, 1, 0 ); // $ExpectError + take.assign( true, [ 1, 3 ], 'throw', out, 1, 0 ); // $ExpectError + take.assign( false, [ 1, 3 ], 'throw', out, 1, 0 ); // $ExpectError + take.assign( null, [ 1, 3 ], 'throw', out, 1, 0 ); // $ExpectError + take.assign( void 0, [ 1, 3 ], 'throw', out, 1, 0 ); // $ExpectError + take.assign( {}, [ 1, 3 ], 'throw', out, 1, 0 ); // $ExpectError +} + +// The compiler throws an error if the `assign` method is provided a second argument which is not an array-like object containing numbers... +{ + const out = [ 0, 0, 0, 0 ]; + + take.assign( [], 1, 'throw', out, 1, 0 ); // $ExpectError + take.assign( [], true, 'throw', out, 1, 0 ); // $ExpectError + take.assign( [], false, 'throw', out, 1, 0 ); // $ExpectError + take.assign( [], null, 'throw', out, 1, 0 ); // $ExpectError + take.assign( [], void 0, 'throw', out, 1, 0 ); // $ExpectError + take.assign( [], {}, 'throw', out, 1, 0 ); // $ExpectError +} + +// The compiler throws an error if the `assign` method is provided a third argument which is not a valid index mode... +{ + const out = [ 0, 0, 0, 0 ]; + + take.assign( [], [ 1, 3 ], '1', out, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 1, out, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], true, out, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], false, out, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], null, out, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], void 0, out, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], {}, out, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], [], out, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], ( x: number ): number => x, out, 1, 0 ); // $ExpectError +} + +// The compiler throws an error if the `assign` method is provided a fourth argument which is not an array-like object... +{ + take.assign( [], [ 1, 3 ], 'throw', 1, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', true, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', false, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', null, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', void 0, 1, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', {}, 1, 0 ); // $ExpectError +} + +// The compiler throws an error if the `assign` method is provided a fifth argument which is not a number... +{ + const out = [ 0, 0, 0, 0 ]; + + take.assign( [], [ 1, 3 ], 'throw', out, '1', 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, true, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, false, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, null, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, void 0, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, {}, 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, [], 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, ( x: number ): number => x, 0 ); // $ExpectError +} + +// The compiler throws an error if the `assign` method is provided a sixth argument which is not a number... +{ + const out = [ 0, 0, 0, 0 ]; + + take.assign( [], [ 1, 3 ], 'throw', out, 1, '1' ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, 1, true ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, 1, false ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, 1, null ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, 1, void 0 ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, 1, {} ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, 1, [] ); // $ExpectError + take.assign( [], [ 1, 3 ], 'throw', out, 1, ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the `assign` method is provided an unsupported number of arguments... +{ + take.assign(); // $ExpectError + take.assign( [] ); // $ExpectError + take.assign( [], [] ); // $ExpectError + take.assign( [], [], 'throw' ); // $ExpectError + take.assign( [], [], 'throw', [] ); // $ExpectError + take.assign( [], [], 'throw', [], 1 ); // $ExpectError + take.assign( [], [], 'throw', [], 1, 0, {} ); // $ExpectError } diff --git a/base/take/examples/index.js b/base/take/examples/index.js index 08d52623..bf4e5302 100644 --- a/base/take/examples/index.js +++ b/base/take/examples/index.js @@ -31,7 +31,7 @@ var N = discreteUniform( 5, 15 ); var indices = filledBy( N, discreteUniform.factory( 0, x.length-1 ) ); // Take a random sample of elements from `x`: -var y = take( x, indices ); +var y = take( x, indices, 'throw' ); console.log( x ); console.log( indices ); diff --git a/base/take/lib/assign.js b/base/take/lib/assign.js new file mode 100644 index 00000000..3163ee44 --- /dev/null +++ b/base/take/lib/assign.js @@ -0,0 +1,250 @@ +/** +* @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 isComplexDataType = require( './../../../base/assert/is-complex-floating-point-data-type' ); +var arraylike2object = require( './../../../base/arraylike2object' ); +var reinterpret = require( '@stdlib/strided/base/reinterpret-complex' ); +var ind = require( '@stdlib/ndarray/base/ind' ).factory; + + +// FUNCTIONS // + +/** +* Takes elements from an indexed array and assigns the values to elements in an indexed output array. +* +* @private +* @param {Collection} x - input array +* @param {IntegerArray} indices - list of indices +* @param {string} mode - index mode +* @param {Collection} out - output array +* @param {integer} stride - output array stride +* @param {NonNegativeInteger} offset - output array offset +* @returns {Collection} output array +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* var indices = [ 3, 1, 2, 0 ]; +* +* var out = [ 0, 0, 0, 0 ]; +* +* var arr = indexed( x, indices, 'throw', out, 1, 0 ); +* // returns [ 4, 2, 3, 1 ] +*/ +function indexed( x, indices, mode, out, stride, offset ) { + var getIndex; + var max; + var io; + var i; + var j; + + // Resolve a function for returning an index according to the specified index mode: + getIndex = ind( mode ); + + // Resolve the maximum index: + max = x.length - 1; + + // Extract each desired element from the provided array... + io = offset; + for ( i = 0; i < indices.length; i++ ) { + j = getIndex( indices[ i ], max ); + out[ io ] = x[ j ]; + io += stride; + } + return out; +} + +/** +* Takes elements from an accessor array and assigns the values to elements in an accessor output array. +* +* @private +* @param {Object} x - input array object +* @param {IntegerArray} indices - index array object +* @param {string} mode - index mode +* @param {Object} out - output array object +* @param {integer} stride - output array stride +* @param {NonNegativeInteger} offset - output array offset +* @returns {Collection} output array +* +* @example +* var toAccessorArray = require( '@stdlib/array/base/to-accessor-array' ); +* var arraylike2object = require( '@stdlib/array/base/arraylike2object' ); +* +* var x = toAccessorArray( [ 1, 2, 3, 4 ] ); +* var indices = toAccessorArray( [ 3, 1, 2, 0 ] ); +* +* var out = toAccessorArray( [ 0, 0, 0, 0 ] ); +* var arr = accessors( arraylike2object( x ), arraylike2object( indices ), 'throw', arraylike2object( out ), 1, 0 ); +* +* var v = arr.get( 0 ); +* // returns 4 +*/ +function accessors( x, indices, mode, out, stride, offset ) { + var getIndex; + var xdata; + var idata; + var odata; + var xget; + var iget; + var oset; + var max; + var io; + var i; + var j; + + xdata = x.data; + idata = indices.data; + odata = out.data; + + xget = x.accessors[ 0 ]; + iget = indices.accessors[ 0 ]; + oset = out.accessors[ 1 ]; + + // Resolve a function for returning an index according to the specified index mode: + getIndex = ind( mode ); + + // Resolve the maximum index: + max = xdata.length - 1; + + // Extract each desired element from the provided array... + io = offset; + for ( i = 0; i < idata.length; i++ ) { + j = getIndex( iget( idata, i ), max ); + oset( odata, io, xget( xdata, j ) ); + io += stride; + } + return odata; +} + +/** +* Takes elements from a complex array and assigns the values to elements in a complex output array. +* +* @private +* @param {Collection} x - real-valued floating-point input array view +* @param {Object} indices - index array object +* @param {string} mode - index mode +* @param {Collection} out - real-valued floating-point output array view +* @param {integer} stride - output array stride +* @param {NonNegativeInteger} offset - output array offset +* @returns {Collection} output array view +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var arraylike2object = require( '@stdlib/array/base/arraylike2object' ); +* +* var x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); +* var indices = [ 0, 0, 1, 1 ]; +* +* var out = new Float64Array( 8 ); +* +* var arr = complex( x, arraylike2object( indices ), 'throw', out, 1, 0 ); +* // returns [ 1.0, 2.0, 1.0, 2.0, 3.0, 4.0, 3.0, 4.0 ] +*/ +function complex( x, indices, mode, out, stride, offset ) { + var getIndex; + var idata; + var iget; + var max; + var io; + var so; + var i; + var j; + var k; + + idata = indices.data; + iget = indices.accessors[ 0 ]; + + // Resolve a function for returning an index according to the specified index mode: + getIndex = ind( mode ); + + // Resolve the maximum index: + max = ( x.length/2 ) - 1; // resolve the length of the original complex array + + // Extract each desired element from the provided array... + so = stride * 2; // note: multiply by 2, as real-valued array consists of interleaved real and imaginary components + io = offset * 2; + for ( i = 0; i < idata.length; i++ ) { + j = getIndex( iget( idata, i ), max ); + k = j * 2; + out[ io ] = x[ k ]; + out[ io+1 ] = x[ k+1 ]; + io += so; + } + return out; +} + + +// MAIN // + +/** +* Takes elements from an array and assigns the values to elements in a provided output array. +* +* @param {Collection} x - input array +* @param {IntegerArray} indices - list of indices +* @param {string} mode - index mode +* @param {Collection} out - output array +* @param {integer} stride - output array stride +* @param {NonNegativeInteger} offset - output array offset +* @returns {Collection} output array +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* var indices = [ 3, 1, 2, 0 ]; +* +* var out = [ 0, 0, 0, 0 ]; +* var arr = assign( x, indices, 'throw', out, 1, 0 ); +* // returns [ 4, 2, 3, 1 ] +* +* var bool = ( arr === out ); +* // returns true +*/ +function assign( x, indices, mode, out, stride, offset ) { + var xo; + var io; + var oo; + + xo = arraylike2object( x ); + io = arraylike2object( indices ); + oo = arraylike2object( out ); + if ( + xo.accessorProtocol || + io.accessorProtocol || + oo.accessorProtocol + ) { + // Note: we only explicitly support complex-to-complex, as this function should not be concerned with casting rules, etc. That is left to userland... + if ( + isComplexDataType( xo.dtype ) && + isComplexDataType( oo.dtype ) + ) { + complex( reinterpret( x, 0 ), io, mode, reinterpret( out, 0 ), stride, offset ); // eslint-disable-line max-len + return out; + } + accessors( xo, io, mode, oo, stride, offset ); + return out; + } + indexed( x, indices, mode, out, stride, offset ); + return out; +} + + +// EXPORTS // + +module.exports = assign; diff --git a/base/take/lib/index.js b/base/take/lib/index.js index 2ba2c826..6709f4b7 100644 --- a/base/take/lib/index.js +++ b/base/take/lib/index.js @@ -29,13 +29,34 @@ * var x = [ 1, 2, 3, 4 ]; * * var indices = [ 0, 0, 1, 1, 3, 3 ]; -* var y = take( x, indices ); +* var y = take( x, indices, 'throw' ); * // returns [ 1, 1, 2, 2, 4, 4 ] +* +* @example +* var take = require( '@stdlib/array/base/take' ); +* +* var x = [ 1, 2, 3, 4 ]; +* +* var out = [ 0, 0, 0, 0, 0, 0 ]; +* var indices = [ 0, 0, 1, 1, 3, 3 ]; +* +* var arr = take.assign( x, indices, 'throw', out, 1, 0 ); +* // returns [ 1, 1, 2, 2, 4, 4 ] +* +* var bool = ( arr === out ); +* // returns true */ // MODULES // +var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); var main = require( './main.js' ); +var assign = require( './assign.js' ); + + +// MAIN // + +setReadOnly( main, 'assign', assign ); // EXPORTS // diff --git a/base/take/lib/main.js b/base/take/lib/main.js index 1d442727..33d0762a 100644 --- a/base/take/lib/main.js +++ b/base/take/lib/main.js @@ -21,6 +21,7 @@ // MODULES // var resolveGetter = require( './../../../base/resolve-getter' ); +var ind = require( '@stdlib/ndarray/base/ind' ).factory; // MAIN // @@ -29,28 +30,41 @@ var resolveGetter = require( './../../../base/resolve-getter' ); * Takes elements from an array. * * @param {Collection} x - input array -* @param {NonNegativeIntegerArray} indices - list of indices +* @param {IntegerArray} indices - list of indices +* @param {string} mode - index mode * @returns {Array} output array * * @example * var x = [ 1, 2, 3, 4 ]; * var indices = [ 3, 1, 2, 0 ]; * -* var y = take( x, indices ); +* var y = take( x, indices, 'throw' ); * // returns [ 4, 2, 3, 1 ] */ -function take( x, indices ) { - var get; +function take( x, indices, mode ) { + var getIndex; + var xget; + var iget; var out; + var max; var i; + var j; - // Resolve an accessor for retrieving input array elements: - get = resolveGetter( x ); + // Resolve an accessor for retrieving array elements: + xget = resolveGetter( x ); + iget = resolveGetter( indices ); + + // Resolve a function for returning an index according to the specified index mode: + getIndex = ind( mode ); + + // Resolve the maximum index: + max = x.length - 1; // Extract each desired element from the provided array... out = []; for ( i = 0; i < indices.length; i++ ) { - out.push( get( x, indices[ i ] ) ); // use `Array#push` to ensure "fast" elements + j = getIndex( iget( indices, i ), max ); + out.push( xget( x, j ) ); // use `Array#push` to ensure "fast" elements } return out; } diff --git a/base/take/test/test.assign.js b/base/take/test/test.assign.js new file mode 100644 index 00000000..dca85fbb --- /dev/null +++ b/base/take/test/test.assign.js @@ -0,0 +1,607 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2022 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 Complex128Array = require( './../../../complex128' ); +var Complex64Array = require( './../../../complex64' ); +var Float64Array = require( './../../../float64' ); +var toAccessorArray = require( './../../../base/to-accessor-array' ); +var isSameComplex128Array = require( '@stdlib/assert/is-same-complex128array' ); +var isSameComplex64Array = require( '@stdlib/assert/is-same-complex64array' ); +var zeros = require( './../../../zeros' ); +var take = require( './../lib/assign.js' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof take, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function takes elements from an array (generic)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = [ 1, 2, 3, 4 ]; + + indices = [ 1, 3 ]; + out = zeros( indices.length, 'generic' ); + actual = take( x, indices, 'throw', out, 1, 0 ); + expected = [ 2, 4 ]; + + t.strictEqual( actual, out, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 1, 1, 3, 3 ]; + out = zeros( indices.length*2, 'generic' ); + actual = take( x, indices, 'throw', out, 2, 0 ); + expected = [ 2, 0, 2, 0, 4, 0, 4, 0 ]; + + t.strictEqual( actual, out, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 3, 2, 1, 0 ]; + out = zeros( indices.length, 'generic' ); + actual = take( x, indices, 'throw', out, -1, out.length-1 ); + expected = [ 1, 2, 3, 4 ]; + + t.strictEqual( actual, out, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]; + out = zeros( indices.length+1, 'generic' ); + actual = take( x, indices, 'throw', out, 1, 1 ); + expected = [ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]; + + t.strictEqual( actual, out, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function takes elements from an array (real typed array)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + + indices = [ 1, 3 ]; + out = zeros( indices.length, 'float64' ); + actual = take( x, indices, 'throw', out, 1, 0 ); + expected = new Float64Array( [ 2.0, 4.0 ] ); + + t.strictEqual( actual, out, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 1, 1, 3, 3 ]; + out = zeros( indices.length*2, 'float64' ); + actual = take( x, indices, 'throw', out, 2, 0 ); + expected = new Float64Array( [ 2.0, 0.0, 2.0, 0.0, 4.0, 0.0, 4.0, 0.0 ] ); + + t.strictEqual( actual, out, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 3, 2, 1, 0 ]; + out = zeros( indices.length, 'float64' ); + actual = take( x, indices, 'throw', out, -1, out.length-1 ); + expected = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + + t.strictEqual( actual, out, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]; + out = zeros( indices.length+1, 'float64' ); + actual = take( x, indices, 'throw', out, 1, 1 ); + expected = new Float64Array( [ 0.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0 ] ); // eslint-disable-line max-len + + t.strictEqual( actual, out, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function takes elements from an array (complex typed array)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + + indices = [ 1, 3 ]; + out = zeros( indices.length, 'complex128' ); + actual = take( x, indices, 'throw', out, 1, 0 ); + expected = new Complex128Array( [ 3.0, 4.0, 7.0, 8.0 ] ); + + t.strictEqual( actual, out, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + indices = [ 1, 1, 3, 3 ]; + out = zeros( indices.length*2, 'complex64' ); + actual = take( x, indices, 'throw', out, 2, 0 ); + expected = new Complex64Array( [ 3.0, 4.0, 0.0, 0.0, 3.0, 4.0, 0.0, 0.0, 7.0, 8.0, 0.0, 0.0, 7.0, 8.0, 0.0, 0.0 ] ); // eslint-disable-line max-len + + t.strictEqual( actual, out, 'returns expected value' ); + t.strictEqual( isSameComplex64Array( actual, expected ), true, 'returns expected value' ); + + x = new Complex64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + + indices = [ 3, 2, 1, 0 ]; + out = zeros( indices.length, 'complex128' ); + actual = take( x, indices, 'throw', out, -1, out.length-1 ); + expected = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); // eslint-disable-line max-len + + t.strictEqual( actual, out, 'returns expected value' ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + indices = [ 1, 1, 1, 1 ]; + out = zeros( indices.length+1, 'complex64' ); + actual = take( x, indices, 'throw', out, 1, 1 ); + expected = new Complex64Array( [ 0.0, 0.0, 3.0, 4.0, 3.0, 4.0, 3.0, 4.0, 3.0, 4.0 ] ); // eslint-disable-line max-len + + t.strictEqual( actual, out, 'returns expected value' ); + t.strictEqual( isSameComplex64Array( actual, expected ), true, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function takes elements from an array (accessors)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = toAccessorArray( [ 1, 2, 3, 4 ] ); + + indices = toAccessorArray( [ 1, 3 ] ); + out = toAccessorArray( zeros( indices.length, 'generic' ) ); + actual = take( x, indices, 'throw', out, 1, 0 ); + expected = [ 2, 4 ]; + + t.strictEqual( actual, out, 'returns expected value' ); + isEqual( actual, expected ); + + indices = toAccessorArray( [ 1, 1, 3, 3 ] ); + out = toAccessorArray( zeros( indices.length*2, 'generic' ) ); + actual = take( x, indices, 'throw', out, 2, 0 ); + expected = [ 2, 0, 2, 0, 4, 0, 4, 0 ]; + + t.strictEqual( actual, out, 'returns expected value' ); + isEqual( actual, expected ); + + indices = toAccessorArray( [ 3, 2, 1, 0 ] ); + out = toAccessorArray( zeros( indices.length, 'generic' ) ); + actual = take( x, indices, 'throw', out, -1, out.length-1 ); + expected = [ 1, 2, 3, 4 ]; + + t.strictEqual( actual, out, 'returns expected value' ); + isEqual( actual, expected ); + + indices = toAccessorArray( [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ); + out = toAccessorArray( zeros( indices.length+1, 'generic' ) ); + actual = take( x, indices, 'throw', out, 1, 1 ); + expected = [ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]; + + t.strictEqual( actual, out, 'returns expected value' ); + isEqual( actual, expected ); + + t.end(); + + function isEqual( actual, expected ) { + var i; + for ( i = 0; i < expected.length; i++ ) { + t.strictEqual( actual.get( i ), expected[ i ], 'returns expected value' ); + } + } +}); + +tape( 'the function returns leaves an output array unchanged if provided a second argument which is empty', function test( t ) { + var expected; + var actual; + var out; + var x; + + x = [ 1, 2, 3, 4 ]; + out = [ 0, 0, 0, 0 ]; + expected = [ 0, 0, 0, 0 ]; + actual = take( x, [], 'throw', out, 1, 0 ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = toAccessorArray( [ 1, 2, 3, 4 ] ); + out = [ 0, 0, 0, 0 ]; + expected = [ 0, 0, 0, 0 ]; + actual = take( x, [], 'throw', out, 1, 0 ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + out = [ 0, 0, 0, 0 ]; + expected = [ 0, 0, 0, 0 ]; + actual = take( x, [], 'throw', out, 1, 0 ); + t.deepEqual( actual, expected, 'returns expected value' ); + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + out = new Complex128Array( [ 0.0, 0.0, 0.0, 0.0 ] ); + expected = new Complex128Array( [ 0.0, 0.0, 0.0, 0.0 ] ); + actual = take( x, [], 'throw', out, 1, 0 ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "throw", the function throws an error if provided an out-of-bounds index (generic)', function test( t ) { + var indices; + var out; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ 4, 5, 1, 2 ]; + out = zeros( x.length, 'generic' ); + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'throw', out, 1, 0 ); + } +}); + +tape( 'when the "mode" is "throw", the function throws an error if provided an out-of-bounds index (accessors)', function test( t ) { + var indices; + var out; + var x; + + x = toAccessorArray( [ 1, 2, 3, 4 ] ); + indices = toAccessorArray( [ 4, 5, 1, 2 ] ); + out = toAccessorArray( zeros( x.length, 'generic' ) ); + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'throw', out, 1, 0 ); + } +}); + +tape( 'when the "mode" is "throw", the function throws an error if provided an out-of-bounds index (real typed)', function test( t ) { + var indices; + var out; + var x; + + x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + indices = [ 4, 5, 1, 2 ]; + out = zeros( x.length, 'float64' ); + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'throw', out, 1, 0 ); + } +}); + +tape( 'when the "mode" is "throw", the function throws an error if provided an out-of-bounds index (complex typed)', function test( t ) { + var indices; + var out; + var x; + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + indices = [ 4, 5, 1, 2 ]; + out = zeros( x.length, 'complex128' ); + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'throw', out, 1, 0 ); + } +}); + +tape( 'when the "mode" is "normalize", the function normalizes negative indices (generic)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ -1, -2, -3, -4 ]; + out = zeros( x.length, 'generic' ); + + actual = take( x, indices, 'normalize', out, 1, 0 ); + expected = [ 4, 3, 2, 1 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "normalize", the function normalizes negative indices (accessors)', function test( t ) { + var expected; + var indices; + var out; + var x; + + x = toAccessorArray( [ 1, 2, 3, 4 ] ); + indices = toAccessorArray( [ -1, -2, -3, -4 ] ); + out = zeros( x.length, 'generic' ); + + take( x, indices, 'normalize', toAccessorArray( out ), 1, 0 ); + expected = [ 4, 3, 2, 1 ]; + t.deepEqual( out, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "normalize", the function normalizes negative indices (real typed)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + indices = [ -1, -2, -3, -4 ]; + out = zeros( x.length, 'float64' ); + + actual = take( x, indices, 'normalize', out, 1, 0 ); + expected = new Float64Array( [ 4.0, 3.0, 2.0, 1.0 ] ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "normalize", the function normalizes negative indices (complex typed)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + indices = [ -1, -2 ]; + out = zeros( x.length, 'complex128' ); + + actual = take( x, indices, 'normalize', out, 1, 0 ); + expected = new Complex128Array( [ 3.0, 4.0, 1.0, 2.0 ] ); + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "normalize", the function throws an error if provided an out-of-bounds index (generic)', function test( t ) { + var indices; + var out; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ 4, 5, 1, 2 ]; + out = zeros( x.length, 'generic' ); + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'normalize', out, 1, 0 ); + } +}); + +tape( 'when the "mode" is "normalize", the function throws an error if provided an out-of-bounds index (accessors)', function test( t ) { + var indices; + var out; + var x; + + x = toAccessorArray( [ 1, 2, 3, 4 ] ); + indices = toAccessorArray( [ 4, 5, 1, 2 ] ); + out = toAccessorArray( zeros( x.length, 'generic' ) ); + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'normalize', out, 1, 0 ); + } +}); + +tape( 'when the "mode" is "normalize", the function throws an error if provided an out-of-bounds index (real typed)', function test( t ) { + var indices; + var out; + var x; + + x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + indices = [ 4, 5, 1, 2 ]; + out = zeros( x.length, 'float64' ); + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'normalize', out, 1, 0 ); + } +}); + +tape( 'when the "mode" is "normalize", the function throws an error if provided an out-of-bounds index (complex typed)', function test( t ) { + var indices; + var out; + var x; + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + indices = [ 4, 5, 1, 2 ]; + out = zeros( x.length, 'complex128' ); + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'normalize', out, 1, 0 ); + } +}); + +tape( 'when the "mode" is "clamp", the function clamps out-of-bounds indices (generic)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ -10, 10, -5, 5 ]; + out = zeros( x.length, 'generic' ); + + actual = take( x, indices, 'clamp', out, 1, 0 ); + expected = [ 1, 4, 1, 4 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "clamp", the function clamps out-of-bounds indices (accessors)', function test( t ) { + var expected; + var indices; + var out; + var x; + + x = toAccessorArray( [ 1, 2, 3, 4 ] ); + indices = toAccessorArray( [ -10, 10, -5, 5 ] ); + out = zeros( x.length, 'generic' ); + + take( x, indices, 'clamp', toAccessorArray( out ), 1, 0 ); + expected = [ 1, 4, 1, 4 ]; + t.deepEqual( out, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "clamp", the function clamps out-of-bounds indices (real typed)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + indices = [ -10, 10, -5, 5 ]; + out = zeros( x.length, 'float64' ); + + actual = take( x, indices, 'clamp', out, 1, 0 ); + expected = new Float64Array( [ 1.0, 4.0, 1.0, 4.0 ] ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "clamp", the function clamps out-of-bounds indices (complex typed)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + indices = [ -10, 10, -5, 5 ]; + out = zeros( x.length, 'complex128' ); + + actual = take( x, indices, 'clamp', out, 1, 0 ); + expected = new Complex128Array( [ 1.0, 2.0, 7.0, 8.0, 1.0, 2.0, 7.0, 8.0 ] ); // eslint-disable-line max-len + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "wrap", the function wraps out-of-bounds indices (generic)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ -10, 10, -5, 5 ]; + out = zeros( x.length, 'generic' ); + + actual = take( x, indices, 'wrap', out, 1, 0 ); + expected = [ 3, 3, 4, 2 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "wrap", the function wraps out-of-bounds indices (accessors)', function test( t ) { + var expected; + var indices; + var out; + var x; + + x = toAccessorArray( [ 1, 2, 3, 4 ] ); + indices = toAccessorArray( [ -10, 10, -5, 5 ] ); + out = zeros( x.length, 'generic' ); + + take( x, indices, 'wrap', toAccessorArray( out ), 1, 0 ); + expected = [ 3, 3, 4, 2 ]; + t.deepEqual( out, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "wrap", the function wraps out-of-bounds indices (real typed)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + indices = [ -10, 10, -5, 5 ]; + out = zeros( x.length, 'float64' ); + + actual = take( x, indices, 'wrap', out, 1, 0 ); + expected = new Float64Array( [ 3.0, 3.0, 4.0, 2.0 ] ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "wrap", the function wraps out-of-bounds indices (complex typed)', function test( t ) { + var expected; + var indices; + var actual; + var out; + var x; + + x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + indices = [ -10, 10, -5, 5 ]; + out = zeros( x.length, 'complex128' ); + + actual = take( x, indices, 'wrap', out, 1, 0 ); + expected = new Complex128Array( [ 5.0, 6.0, 5.0, 6.0, 7.0, 8.0, 3.0, 4.0 ] ); // eslint-disable-line max-len + t.strictEqual( isSameComplex128Array( actual, expected ), true, 'returns expected value' ); + + t.end(); +}); diff --git a/base/take/test/test.js b/base/take/test/test.js index 13a65fcc..47d782ac 100644 --- a/base/take/test/test.js +++ b/base/take/test/test.js @@ -22,6 +22,7 @@ var tape = require( 'tape' ); var Complex64Array = require( './../../../complex64' ); +var toAccessorArray = require( './../../../base/to-accessor-array' ); var realf = require( '@stdlib/complex/realf' ); var imagf = require( '@stdlib/complex/imagf' ); var isComplex64 = require( '@stdlib/assert/is-complex64' ); @@ -45,22 +46,22 @@ tape( 'the function takes elements from an array', function test( t ) { x = [ 1, 2, 3, 4 ]; indices = [ 1, 3 ]; - actual = take( x, indices ); + actual = take( x, indices, 'throw' ); expected = [ 2, 4 ]; t.deepEqual( actual, expected, 'returns expected value' ); indices = [ 1, 1, 3, 3 ]; - actual = take( x, indices ); + actual = take( x, indices, 'throw' ); expected = [ 2, 2, 4, 4 ]; t.deepEqual( actual, expected, 'returns expected value' ); indices = [ 3, 2, 1, 0 ]; - actual = take( x, indices ); + actual = take( x, indices, 'throw' ); expected = [ 4, 3, 2, 1 ]; t.deepEqual( actual, expected, 'returns expected value' ); indices = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]; - actual = take( x, indices ); + actual = take( x, indices, 'throw' ); expected = [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]; t.deepEqual( actual, expected, 'returns expected value' ); @@ -76,13 +77,13 @@ tape( 'the function takes elements from an array (accessors)', function test( t var i; x = new Complex64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); - indices = [ 1, 1, 3, 3 ]; - actual = take( x, indices ); + indices = toAccessorArray( [ 1, 1, 3, 3 ] ); + actual = take( x, indices, 'throw' ); t.notEqual( actual, x, 'returns different reference' ); for ( i = 0; i < indices.length; i++ ) { v = actual[ i ]; - expected = x.get( indices[ i ] ); + expected = x.get( indices.get( i ) ); t.strictEqual( isComplex64( v ), true, 'returns expected value' ); t.strictEqual( realf( v ), realf( expected ), 'returns expected value' ); t.strictEqual( imagf( v ), imagf( expected ), 'returns expected value' ); @@ -90,7 +91,28 @@ tape( 'the function takes elements from an array (accessors)', function test( t t.end(); }); -tape( 'the function does not perform bounds checking', function test( t ) { +tape( 'the function returns an empty array if provided a second argument which is empty', function test( t ) { + var x = [ 1, 2, 3, 4 ]; + t.deepEqual( take( x, [], 'throw' ), [], 'returns expected value' ); + t.end(); +}); + +tape( 'when the "mode" is "throw", the function throws an error if provided an out-of-bounds index', function test( t ) { + var indices; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ 4, 5, 1, 2 ]; + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'throw' ); + } +}); + +tape( 'when the "mode" is "normalize", the function normalizes negative indices', function test( t ) { var expected; var indices; var actual; @@ -98,16 +120,57 @@ tape( 'the function does not perform bounds checking', function test( t ) { x = [ 1, 2, 3, 4 ]; - indices = [ 4, 5, 1, 2 ]; - actual = take( x, indices ); - expected = [ void 0, void 0, 2, 3 ]; + indices = [ -1, -2, -3, -4 ]; + actual = take( x, indices, 'normalize' ); + expected = [ 4, 3, 2, 1 ]; t.deepEqual( actual, expected, 'returns expected value' ); t.end(); }); -tape( 'the function returns an empty array if provided a second argument which is empty', function test( t ) { - var x = [ 1, 2, 3, 4 ]; - t.deepEqual( take( x, [] ), [], 'returns expected value' ); +tape( 'when the "mode" is "normalize", the function throws an error if provided an out-of-bounds index', function test( t ) { + var indices; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ 2, 50, 1, 2 ]; + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, 'normalize' ); + } +}); + +tape( 'when the "mode" is "clamp", the function clamps out-of-bounds indices', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = [ 1, 2, 3, 4 ]; + + indices = [ -10, 10, -5, 5 ]; + actual = take( x, indices, 'clamp' ); + expected = [ 1, 4, 1, 4 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "wrap", the function wraps out-of-bounds indices', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = [ 1, 2, 3, 4 ]; + + indices = [ -10, 10, -5, 5 ]; + actual = take( x, indices, 'wrap' ); + expected = [ 3, 3, 4, 2 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + t.end(); }); diff --git a/base/zero-to/lib/assign.js b/base/zero-to/lib/assign.js index f33e39c9..30b48f74 100644 --- a/base/zero-to/lib/assign.js +++ b/base/zero-to/lib/assign.js @@ -20,8 +20,6 @@ // MODULES // -var isComplex128Array = require( './../../../base/assert/is-complex128array' ); -var isComplex64Array = require( './../../../base/assert/is-complex64array' ); var arraylike2object = require( './../../../base/arraylike2object' ); var reinterpret128 = require( '@stdlib/strided/base/reinterpret-complex128' ); var reinterpret64 = require( '@stdlib/strided/base/reinterpret-complex64' ); @@ -181,10 +179,10 @@ function assign( out, stride, offset ) { var obj = arraylike2object( out ); if ( obj.accessorProtocol ) { // If provided a complex number array, reinterpret as a real typed array and only set the real components... - if ( isComplex128Array( out ) ) { + if ( obj.dtype === 'complex128' ) { return complex( out, reinterpret128( out, 0 ), stride, offset ); } - if ( isComplex64Array( out ) ) { + if ( obj.dtype === 'complex64' ) { return complex( out, reinterpret64( out, 0 ), stride, offset ); } return accessors( obj, stride, offset ); diff --git a/index/README.md b/index/README.md new file mode 100644 index 00000000..7eaaafa1 --- /dev/null +++ b/index/README.md @@ -0,0 +1,471 @@ + + +# ArrayIndex + +> Array index constructor. + + + +
+ +In JavaScript, only strings and symbols are valid property names. When providing values for property names which are not string or symbols, the values are serialized to strings **prior to** attempting to access property values. For example, the following + +```javascript +// Create an array: +var x = [ 1, 2, 3, 4 ]; + +// Define a list of indices for elements we want to retrieve from `x`: +var y = [ 0, 2 ]; + +// Attempt to retrieve the desired elements: +var v = x[ y ]; // => desired: [ 1, 3 ] +// returns undefined +``` + +is equivalent to + +```javascript +var x = [ 1, 2, 3, 4 ]; +var y = [ 0, 2 ]; + +var v = x[ y.toString() ]; +// returns undefined + +// ...which is equivalent to: +v = x[ '0,2' ]; +// returns undefined +``` + +Accordingly, in order to circumvent built-in property access behavior and support non-traditional access patterns, one can leverage [`Proxy`][@stdlib/proxy/ctor] objects which allow one to intercept property access and to perform transformations before attempting to access elements in a target object. + +To support the access pattern shown in the example above, one can leverage built-in string serialization behavior to reconstruct the original property value provided prior to serialization. The `ArrayIndex` constructor described below provides one such mechanism. + +Specifically, instantiated `ArrayIndex` objects are assigned a unique identifier and stored in a local cache. When provided as property values to `ArrayIndex` consumers, instantiated objects serialize to a string containing their unique identifier. `ArrayIndex` consumers can then parse the serialized string to obtain the unique identifier and subsequently recover the original array from the local cache. + +
+ + + + + +
+ +## Usage + +```javascript +var ArrayIndex = require( '@stdlib/array/index' ); +``` + + + +#### ArrayIndex( x\[, options] ) + +Wraps a provided array as an array index object. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var idx = new ArrayIndex( x ); +// returns +``` + +The constructor accepts the following arguments: + +- **x**: input array. +- **options**: function options. + +The constructor accepts the following options: + +- **persist**: boolean indicating whether to continue persisting an index object after first usage. Default: `false`. + +By default, an `ArrayIndex` is invalidated and removed from an internal cache immediately after a consumer resolves the underlying data associated with an `ArrayIndex` instance using the [`ArrayIndex.get()`](#static-method-get) static method. Immediate invalidation and cache removal ensures that references to the underlying array are not the source of memory leaks. + +One may, however, want to reuse an `ArrayIndex` instance to avoid additional memory allocation. In order to persist an `ArrayIndex` and prevent automatic cache invalidation, set the `persist` option to `true`. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var idx = new ArrayIndex( x, { + 'persist': true +}); +// returns + +// ... + +var o = ArrayIndex.get( idx.id ); +// returns {...} + +// ... + +o = ArrayIndex.get( idx.id ); +// returns {...} + +// ... + +// Explicitly free the array index: +ArrayIndex.free( idx.id ); +``` + +In order to **prevent** memory leaks when working with persisted `ArrayIndex` instances, one **must** remember to manually free persisted instances using the [`ArrayIndex.free()`](#static-method-free) method. + +* * * + +### Properties + + + +#### ArrayIndex.name + +String value of the `ArrayIndex` constructor name. + +```javascript +var str = ArrayIndex.name; +// returns 'ArrayIndex' +``` + + + +#### ArrayIndex.prototype.data + +**Read-only** property returning the underlying array associated with an `ArrayIndex` instance. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +// returns + +var v = idx.data; +// returns [ 1, 0, 1, 0 ] +``` + + + +#### ArrayIndex.prototype.dtype + +**Read-only** property returning the data type of the underlying array associated with an `ArrayIndex` instance. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +// returns + +var dt = idx.dtype; +// returns 'uint8' +``` + + + +#### ArrayIndex.prototype.id + +**Read-only** property returning the unique identifier associated with an `ArrayIndex` instance. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +// returns + +var id = idx.id; +// returns +``` + +The identifier should be used by `ArrayIndex` consumers to resolve the underlying data associated with an `ArrayIndex` instance. + + + +#### ArrayIndex.prototype.isCached + +**Read-only** property returning a boolean indicating whether an `ArrayIndex` instance is actively cached. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +// returns + +var out = idx.isCached; +// returns true +``` + + + +#### ArrayIndex.prototype.type + +**Read-only** property returning the array index type. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +// returns + +var t = idx.type; +// returns 'mask' +``` + +The following array index types are supported: + +- **mask**: mask array, in which a value of zero indicates to include a respective element and a value of one indicates to exclude a respective element. A mask array is the complement of a boolean array. +- **bool**: boolean array, in which a value of `true` indicates to include a respective element and a value of `false` indicates to exclude a respective element. A boolean array is the complement of a mask array. +- **int**: integer array, in which each element is an index indicating the position of an element to include. Elements are **not** required to be unique (i.e., more than element may resolve to the same position). + +* * * + +### Methods + + + +#### ArrayIndex.free( id ) + +Frees the `ArrayIndex` associated with a provided identifier. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ), { + 'persist': true +}); +// returns + +// ... + +var out = ArrayIndex.free( idx.id ); +// returns true +``` + +Once an `ArrayIndex` is freed, the instance is invalid and can no longer be used. Any subsequent `ArrayIndex` operations (i.e., property and method access) will raise an exception. + + + +#### ArrayIndex.get( id ) + +Returns the array associated with the `ArrayIndex` having a provided identifier. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ), { + 'persist': true +}); +// returns + +// ... + +var o = ArrayIndex.get( idx.id ); +// returns {...} + +var d = o.data; +// returns [ 1, 0, 1, 0 ] + +var t = o.type; +// returns 'mask' + +var dt = o.dtype; +// returns 'uint8' +``` + +The returned object has the following properties: + +- **data**: the underlying array associated with the `ArrayIndex` identified by the provided `id`. +- **type**: the type of array index. One of the following: `'int'`, `'bool'`, or `'mask'`. +- **dtype**: the data type of the underlying array. + +If the `ArrayIndex` associated with a provided identifier was not explicitly persisted, calling this method will cause the `ArrayIndex` to be invalidated and removed from an internal cache. Any subsequent instance operations (i.e., property and method access) will raise an exception. + + + +#### ArrayIndex.prototype.toString() + +Serializes an `ArrayIndex` as a string. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +// returns + +var str = idx.toString(); +// e.g., 'ArrayIndex<0>' +``` + +An `ArrayIndex` is intended to be an opaque object used by objects supporting "fancy" indexing (e.g., [fancy arrays][@stdlib/array/to-fancy]). As such, when serialized as a string, a serialized `ArrayIndex` includes only the unique identifier associated with the respective instance. + + + +#### ArrayIndex.prototype.toJSON() + +Serializes an `ArrayIndex` as a [JSON][json] object. + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); + +var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +// returns + +var o = idx.toJSON(); +// returns { 'type': 'ArrayIndex', 'data': { 'type': 'Uint8Array', 'data': [ 1, 0, 1, 0 ] } } +``` + +`JSON.stringify()` implicitly calls this method when stringifying an `ArrayIndex` instance. + +
+ + + + + +* * * + +
+ +## Notes + +- `ArrayIndex` instances have no explicit functionality; however, they are used by ["fancy" arrays][@stdlib/array/to-fancy] and other packages for element retrieval and assignment. + +- Because `ArrayIndex` instances leverage an internal cache implementing the **Singleton pattern**, one **must** be sure to use the same `ArrayIndex` constructor as `ArrayIndex` consumers. If one uses a different `ArrayIndex` constructor, the consumer will **not** be able to resolve the original wrapped array, as the consumer will attempt to resolve an `ArrayIndex` instance in the wrong internal cache. + +- Because non-persisted `ArrayIndex` instances are freed after first use, in order to avoid holding onto memory and to allow garbage collection, one should avoid scenarios in which an `ArrayIndex` is never used. For example, + + ```javascript + var Uint8Array = require( '@stdlib/array/uint8' ); + + var data = new Uint8Array( [ 1, 0, 0, 0 ] ); + var idx = new ArrayIndex( data ); + + var o; + if ( data[ 0 ] === 0 ) { + // Do something with `idx`... + o = ArrayIndex.get( idx.id ); + + // ... + } + ``` + + will leak memory as `idx` is only consumed within an `if` block which never evaluates. In such scenarios, one should either refactor to avoid inadvertently holding onto memory or explicitly free the `ArrayIndex`. + + ```javascript + var Uint8Array = require( '@stdlib/array/uint8' ); + + var data = new Uint8Array( [ 1, 0, 0, 0 ] ); + var idx = new ArrayIndex( data ); + + var o; + if ( data[ 0 ] === 0 ) { + // Do something with `idx`... + o = ArrayIndex.get( idx.id ); + + // ... + } else { + ArrayIndex.free( idx.id ); + } + ``` + +
+ + + + + +* * * + +
+ +## Examples + + + +```javascript +var Uint8Array = require( '@stdlib/array/uint8' ); +var Int32Array = require( '@stdlib/array/int32' ); +var ArrayIndex = require( '@stdlib/array/index' ); + +var x = new Uint8Array( [ 1, 0, 1, 0 ] ); +var i = new ArrayIndex( x ); +// returns + +var o = ArrayIndex.get( i.id ); +// returns {...} + +console.log( 'Type: %s. Data type: %s.', o.type, o.dtype ); + +x = [ true, false, true, false ]; +i = new ArrayIndex( x ); +// returns + +o = ArrayIndex.get( i.id ); +// returns {...} + +console.log( 'Type: %s. Data type: %s.', o.type, o.dtype ); + +x = new Int32Array( [ 1, 3, 4, 7 ] ); +i = new ArrayIndex( x ); +// returns + +o = ArrayIndex.get( i.id ); +// returns {...} + +console.log( 'Type: %s. Data type: %s.', o.type, o.dtype ); + +x = [ 1, 3, 4, 7 ]; +i = new ArrayIndex( x ); +// returns + +o = ArrayIndex.get( i.id ); +// returns {...} + +console.log( 'Type: %s. Data type: %s.', o.type, o.dtype ); +``` + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + diff --git a/index/benchmark/benchmark.data.js b/index/benchmark/benchmark.data.js new file mode 100644 index 00000000..b1ff8301 --- /dev/null +++ b/index/benchmark/benchmark.data.js @@ -0,0 +1,60 @@ +/** +* @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 isCollection = require( '@stdlib/assert/is-collection' ); +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+':data', function benchmark( b ) { + var values; + var opts; + var v; + var i; + + opts = { + 'persist': true + }; + + values = [ + new ArrayIndex( [ 1, 2, 3 ], opts ), + new ArrayIndex( [ 5, 6, 7 ], opts ), + new ArrayIndex( [ true, false, true ], opts ) + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = values[ i%values.length ].data; + if ( typeof v !== 'object' ) { + b.fail( 'should return an object' ); + } + } + b.toc(); + if ( !isCollection( v ) ) { + b.fail( 'should return a collection' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/benchmark/benchmark.dtype.js b/index/benchmark/benchmark.dtype.js new file mode 100644 index 00000000..5a9eea9f --- /dev/null +++ b/index/benchmark/benchmark.dtype.js @@ -0,0 +1,60 @@ +/** +* @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 isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+':dtype', function benchmark( b ) { + var values; + var opts; + var v; + var i; + + opts = { + 'persist': true + }; + + values = [ + new ArrayIndex( [ 1, 2, 3 ], opts ), + new ArrayIndex( [ 5, 6, 7 ], opts ), + new ArrayIndex( [ true, false, true ], opts ) + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = values[ i%values.length ].dtype; + if ( typeof v !== 'string' ) { + b.fail( 'should return a string' ); + } + } + b.toc(); + if ( !isString( v ) ) { + b.fail( 'should return a string' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/benchmark/benchmark.get.js b/index/benchmark/benchmark.get.js new file mode 100644 index 00000000..0bdb0bde --- /dev/null +++ b/index/benchmark/benchmark.get.js @@ -0,0 +1,60 @@ +/** +* @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 isPlainObject = require( '@stdlib/assert/is-plain-object' ); +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+':get', function benchmark( b ) { + var values; + var opts; + var v; + var i; + + opts = { + 'persist': true + }; + + values = [ + ( new ArrayIndex( [ 1, 2, 3 ], opts ) ).id, + ( new ArrayIndex( [ 5, 6, 7 ], opts ) ).id, + ( new ArrayIndex( [ true, false, true ], opts ) ).id + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = ArrayIndex.get( values[ i%values.length ] ); + if ( typeof v !== 'object' ) { + b.fail( 'should return an object' ); + } + } + b.toc(); + if ( !isPlainObject( v ) ) { + b.fail( 'should return an object' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/benchmark/benchmark.id.js b/index/benchmark/benchmark.id.js new file mode 100644 index 00000000..010e132e --- /dev/null +++ b/index/benchmark/benchmark.id.js @@ -0,0 +1,60 @@ +/** +* @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 isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+':id', function benchmark( b ) { + var values; + var opts; + var v; + var i; + + opts = { + 'persist': true + }; + + values = [ + new ArrayIndex( [ 1, 2, 3 ], opts ), + new ArrayIndex( [ 5, 6, 7 ], opts ), + new ArrayIndex( [ true, false, true ], opts ) + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = values[ i%values.length ].id; + if ( typeof v !== 'string' ) { + b.fail( 'should return a string' ); + } + } + b.toc(); + if ( !isString( v ) ) { + b.fail( 'should return a string' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/benchmark/benchmark.is_cached.js b/index/benchmark/benchmark.is_cached.js new file mode 100644 index 00000000..c4bccf03 --- /dev/null +++ b/index/benchmark/benchmark.is_cached.js @@ -0,0 +1,60 @@ +/** +* @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 isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+':isCached', function benchmark( b ) { + var values; + var opts; + var v; + var i; + + opts = { + 'persist': true + }; + + values = [ + new ArrayIndex( [ 1, 2, 3 ], opts ), + new ArrayIndex( [ 5, 6, 7 ], opts ), + new ArrayIndex( [ true, false, true ], opts ) + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = values[ i%values.length ].isCached; + if ( typeof v !== 'boolean' ) { + b.fail( 'should return a boolean' ); + } + } + b.toc(); + if ( !isBoolean( v ) ) { + b.fail( 'should return a boolean' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/benchmark/benchmark.js b/index/benchmark/benchmark.js new file mode 100644 index 00000000..14c89bce --- /dev/null +++ b/index/benchmark/benchmark.js @@ -0,0 +1,84 @@ +/** +* @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 instanceOf = require( '@stdlib/assert/instance-of' ); +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+'::instantiation,new', function benchmark( b ) { + var values; + var v; + var i; + + values = [ + [ 1, 2, 3 ], + [ 5, 6, 7 ], + [ true, false, true ] + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = new ArrayIndex( values[ i%values.length ] ); + if ( typeof v !== 'object' ) { + b.fail( 'should return an object' ); + } + } + b.toc(); + if ( !instanceOf( v, ArrayIndex ) ) { + b.fail( 'should return an instance' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); + +bench( pkg+'::instantiation,no_new', function benchmark( b ) { + var values; + var idx; + var v; + var i; + + idx = ArrayIndex; + + values = [ + [ 1, 2, 3 ], + [ 5, 6, 7 ], + [ true, false, true ] + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = idx( values[ i%values.length ] ); + if ( typeof v !== 'object' ) { + b.fail( 'should return an object' ); + } + } + b.toc(); + if ( !instanceOf( v, ArrayIndex ) ) { + b.fail( 'should return an instance' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/benchmark/benchmark.to_json.js b/index/benchmark/benchmark.to_json.js new file mode 100644 index 00000000..3c99ab76 --- /dev/null +++ b/index/benchmark/benchmark.to_json.js @@ -0,0 +1,60 @@ +/** +* @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 isPlainObject = require( '@stdlib/assert/is-plain-object' ); +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+':toJSON:len=3', function benchmark( b ) { + var values; + var opts; + var v; + var i; + + opts = { + 'persist': true + }; + + values = [ + new ArrayIndex( [ 1, 2, 3 ], opts ), + new ArrayIndex( [ 5, 6, 7 ], opts ), + new ArrayIndex( [ true, false, true ], opts ) + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = values[ i%values.length ].toJSON(); + if ( typeof v !== 'object' ) { + b.fail( 'should return an object' ); + } + } + b.toc(); + if ( !isPlainObject( v ) ) { + b.fail( 'should return an object' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/benchmark/benchmark.to_string.js b/index/benchmark/benchmark.to_string.js new file mode 100644 index 00000000..e973acb3 --- /dev/null +++ b/index/benchmark/benchmark.to_string.js @@ -0,0 +1,60 @@ +/** +* @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 isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+':toString:len=3', function benchmark( b ) { + var values; + var opts; + var v; + var i; + + opts = { + 'persist': true + }; + + values = [ + new ArrayIndex( [ 1, 2, 3 ], opts ), + new ArrayIndex( [ 5, 6, 7 ], opts ), + new ArrayIndex( [ true, false, true ], opts ) + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = values[ i%values.length ].toString(); + if ( typeof v !== 'string' ) { + b.fail( 'should return a string' ); + } + } + b.toc(); + if ( !isString( v ) ) { + b.fail( 'should return a string' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/benchmark/benchmark.type.js b/index/benchmark/benchmark.type.js new file mode 100644 index 00000000..fa3f11e4 --- /dev/null +++ b/index/benchmark/benchmark.type.js @@ -0,0 +1,60 @@ +/** +* @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 isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var pkg = require( './../package.json' ).name; +var ArrayIndex = require( './../lib' ); + + +// MAIN // + +bench( pkg+':type', function benchmark( b ) { + var values; + var opts; + var v; + var i; + + opts = { + 'persist': true + }; + + values = [ + new ArrayIndex( [ 1, 2, 3 ], opts ), + new ArrayIndex( [ 5, 6, 7 ], opts ), + new ArrayIndex( [ true, false, true ], opts ) + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = values[ i%values.length ].type; + if ( typeof v !== 'string' ) { + b.fail( 'should return a string' ); + } + } + b.toc(); + if ( !isString( v ) ) { + b.fail( 'should return a string' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/index/docs/repl.txt b/index/docs/repl.txt new file mode 100644 index 00000000..d2e4cd7a --- /dev/null +++ b/index/docs/repl.txt @@ -0,0 +1,202 @@ + +{{alias}}( x[, options] ) + Wraps a provided array as an array index object. + + Array index instances have no explicit functionality; however, they are used + by "fancy" arrays for element retrieval and assignment. + + By default, an instance is invalidated and removed from an internal cache + immediately after a consumer resolves the underlying data associated with an + instance using the `get` static method. Immediate invalidation and cache + removal ensures that references to the underlying array are not the source + of memory leaks. + + Because instances leverage an internal cache implementing the Singleton + pattern, one must be sure to use the same constructor as consumers. If one + uses a different constructor, the consumer will *not* be able to resolve the + original wrapped array, as the consumer will attempt to resolve an instance + in the wrong internal cache. + + Because non-persisted instances are freed after first use, in order to avoid + holding onto memory and to allow garbage collection, one should avoid + scenarios in which an instance is never used. + + Parameters + ---------- + x: Array|TypedArray|Object + Input array. + + options: Object (optional) + Function options. + + options.persist: boolean (optional) + Boolean indicating whether to continue persisting an index object after + first usage. Default: false. + + Returns + ------- + out: ArrayIndex + ArrayIndex instance. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + + +{{alias}}.free( id ) + Frees the instance associated with a provided identifier. + + Parameters + ---------- + id: string + Instance identifier. + + Returns + ------- + out: boolean + Boolean indicating whether an instance was successfully freed. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > // ... + > {{alias}}.free( idx.id ) + + +{{alias}}.get( id ) + Returns the array associated with the instance having a provided identifier. + + Parameters + ---------- + id: string + Instance identifier. + + Returns + ------- + out: Object + Object containing array data. + + out.data: Array|TypedArray|Object + The underlying array associated with the provided identifier. + + out.type: string + The type of array index. + + out.dtype: string + The data type of the underlying array. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > {{alias}}.get( idx.id ) + {...} + + +{{alias}}.prototype.data + Read-only property returning the underlying index array. + + Returns + ------- + out: Array|TypedArray|Object + Array data type. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > idx.data + [ 1, 2, 3, 4 ] + + +{{alias}}.prototype.dtype + Read-only property returning the underlying data type of the index array. + + Returns + ------- + out: string + Array data type. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > idx.dtype + 'generic' + + +{{alias}}.prototype.id + Read-only property returning the unique identifier associated with an + instance. + + Returns + ------- + out: string + String identifier. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > idx.id + + + +{{alias}}.prototype.isCached + Read-only property returning a boolean indicating whether an array index is + actively cached. + + Returns + ------- + out: boolean + Boolean indicating whether an array index is actively cached. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > idx.isCached + true + + +{{alias}}.prototype.type + Read-only property returning the array index type. + + Returns + ------- + out: string + Array index type. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > idx.type + + + +{{alias}}.prototype.toString() + Serializes an instance as a string. + + Returns + ------- + str: string + Serialized string. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > idx.toString() + + +{{alias}}.prototype.toJSON() + Serializes an instance as a JSON object. + + Returns + ------- + obj: Object + JSON object. + + Examples + -------- + > var idx = new {{alias}}( [ 1, 2, 3, 4 ] ); + > idx.toJSON() + { 'type': 'ArrayIndex', 'data': [ 1, 2, 3, 4 ] } + + See Also + -------- + diff --git a/index/docs/types/index.d.ts b/index/docs/types/index.d.ts new file mode 100644 index 00000000..11fa91bb --- /dev/null +++ b/index/docs/types/index.d.ts @@ -0,0 +1,539 @@ +/* +* @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, DataType } from '@stdlib/types/array'; + +/** +* Boolean index array. +*/ +type BooleanIndexArray = Collection | AccessorArrayLike; + +/** +* Integer index array. +*/ +type IntegerIndexArray = Collection | AccessorArrayLike; + +/** +* Index array. +*/ +type IndexArray = BooleanIndexArray | IntegerIndexArray; + +/** +* Interface describing function options. +*/ +interface Options { + /** + * Boolean indicating whether to continue persisting an index object after first usage (default: `false`). + */ + persist?: boolean; +} + +/** +* Interface describing an object containing array index data. +*/ +interface BaseArrayObject { + /** + * The underlying array associated with an array index. + */ + data: IndexArray; + + /** + * The type of array index. + */ + type: 'int' | 'bool' | 'mask'; + + /** + * The data type of the underlying array. + */ + dtype: DataType; +} + +/** +* Interface describing an object containing mask array data. +*/ +interface MaskArrayObject extends BaseArrayObject { + /** + * The underlying array associated with an array index. + */ + data: Uint8Array; + + /** + * The type of array index. + */ + type: 'mask'; + + /** + * The data type of the underlying array. + */ + dtype: 'uint8'; +} + +/** +* Interface describing an object containing integer array data. +*/ +interface Int32ArrayObject extends BaseArrayObject { + /** + * The underlying array associated with an array index. + */ + data: Int32Array; + + /** + * The type of array index. + */ + type: 'int'; + + /** + * The data type of the underlying array. + */ + dtype: 'int32'; +} + +/** +* Interface describing an object containing integer array data. +*/ +interface IntegerArrayObject extends BaseArrayObject { + /** + * The underlying array associated with an array index. + */ + data: IntegerIndexArray; + + /** + * The type of array index. + */ + type: 'int'; + + /** + * The data type of the underlying array. + */ + dtype: 'generic'; +} + +/** +* Interface describing an object containing boolean array data. +*/ +interface BooleanArrayObject extends BaseArrayObject { + /** + * The underlying array associated with an array index. + */ + data: BooleanIndexArray; + + /** + * The type of array index. + */ + type: 'bool'; + + /** + * The data type of the underlying array. + */ + dtype: 'generic'; +} + +/** +* Array object data. +*/ +type ArrayObject = MaskArrayObject | Int32ArrayObject | BooleanArrayObject | IntegerArrayObject | null; + +/** +* Interface describing an array index object. +*/ +interface BaseArrayIndex { + /** + * Read-only property returning the data associated with an `ArrayIndex` instance. + */ + data: IndexArray; + + /** + * Read-only property returning the underlying array index data type. + */ + dtype: DataType; + + /** + * Read-only property returning the unique identifier associated with an `ArrayIndex` instance. + */ + id: string; + + /** + * Boolean indicating if an `ArrayIndex` instance is actively cached. + */ + isCached: boolean; + + /** + * Read-only property returning the array index type. + */ + type: 'int' | 'bool' | 'mask'; + + /** + * Serializes an `ArrayIndex` to a string. + * + * @returns serialized string + * + * @example + * var Uint8Array = require( './../../../uint8' ); + * + * var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); + * // returns + * + * var str = idx.toString(); + * // e.g., 'ArrayIndex<0>' + */ + toString(): string; +} + +/** +* Interface describing a mask array index object. +*/ +interface MaskArrayIndex extends BaseArrayIndex { + /** + * Read-only property returning the array index type. + */ + type: 'mask'; + + /** + * Read-only property returning the underlying array index data type. + */ + dtype: 'uint8'; + + /** + * Read-only property returning the underlying array data. + */ + data: Uint8Array; +} + +/** +* Interface describing an integer array index object. +*/ +interface Int32ArrayIndex extends BaseArrayIndex { + /** + * Read-only property returning the array index type. + */ + type: 'int'; + + /** + * Read-only property returning the underlying array index data type. + */ + dtype: 'int32'; + + /** + * Read-only property returning the underlying array data. + */ + data: Int32Array; +} + +/** +* Interface describing a boolean array index object. +*/ +interface BooleanArrayIndex extends BaseArrayIndex { + /** + * Read-only property returning the array index type. + */ + type: 'bool'; + + /** + * Read-only property returning the underlying array index data type. + */ + dtype: 'generic'; + + /** + * Read-only property returning the underlying array data. + */ + data: BooleanIndexArray; +} + +/** +* Interface describing an integer array index object. +*/ +interface IntegerArrayIndex extends BaseArrayIndex { + /** + * Read-only property returning the array index type. + */ + type: 'int'; + + /** + * Read-only property returning the underlying array index data type. + */ + dtype: 'generic'; + + /** + * Read-only property returning the underlying array data. + */ + data: IntegerIndexArray; +} + +/** +* Array index object. +*/ +type ArrayIndex = MaskArrayIndex | Int32ArrayIndex | BooleanArrayIndex | IntegerArrayIndex; + +/** +* Interface defining an `ArrayIndex` constructor which is both "newable" and "callable". +*/ +interface Constructor { + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var Uint8Array = require( './../../../uint8' ); + * + * var x = new Uint8Array( [ 1, 0, 1, 0 ] ); + * + * var idx = new ArrayIndex( x ); + * // returns + */ + new( x: Uint8Array, options?: Options ): MaskArrayIndex; + + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var Int32Array = require( './../../../int32' ); + * + * var x = new Int32Array( [ 1, 0, 1, 0 ] ); + * + * var idx = new ArrayIndex( x ); + * // returns + */ + new( x: Int32Array, options?: Options ): Int32ArrayIndex; + + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var x = [ 1, 2, 3, 4 ]; + * + * var idx = new ArrayIndex( x ); + * // returns + */ + new( x: IntegerIndexArray, options?: Options ): IntegerArrayIndex; + + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var x = [ true, false, true, false ]; + * + * var idx = new ArrayIndex( x ); + * // returns + */ + new( x: BooleanIndexArray, options?: Options ): BooleanArrayIndex; + + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var Uint8Array = require( './../../../uint8' ); + * + * var x = new Uint8Array( [ 1, 0, 1, 0 ] ); + * + * var idx = new ArrayIndex( x ); + * // returns + */ + new( x: IndexArray, options?: Options ): ArrayIndex; + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var Uint8Array = require( './../../../uint8' ); + * + * var x = new Uint8Array( [ 1, 0, 1, 0 ] ); + * + * var idx = ArrayIndex( x ); + * // returns + */ + ( x: Uint8Array, options?: Options ): MaskArrayIndex; + + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var Int32Array = require( './../../../int32' ); + * + * var x = new Int32Array( [ 1, 0, 1, 0 ] ); + * + * var idx = ArrayIndex( x ); + * // returns + */ + ( x: Int32Array, options?: Options ): Int32ArrayIndex; + + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var x = [ 1, 2, 3, 4 ]; + * + * var idx = ArrayIndex( x ); + * // returns + */ + ( x: IntegerIndexArray, options?: Options ): IntegerArrayIndex; + + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var x = [ true, false, true, false ]; + * + * var idx = ArrayIndex( x ); + * // returns + */ + ( x: BooleanIndexArray, options?: Options ): BooleanArrayIndex; + + /** + * Array index constructor. + * + * @param x - input array + * @param options - function options + * @param options.persist - boolean indicating whether to continue persisting an index object after first usage + * @returns ArrayIndex instance + * + * @example + * var Uint8Array = require( './../../../uint8' ); + * + * var x = new Uint8Array( [ 1, 0, 1, 0 ] ); + * + * var idx = ArrayIndex( x ); + * // returns + */ + ( x: IndexArray, options?: Options ): ArrayIndex; + + /** + * String value of the constructor name. + */ + name: 'ArrayIndex'; + + /** + * Frees the `ArrayIndex` associated with a provided identifier. + * + * @param id - identifier + * @returns boolean indicating whether an `ArrayIndex` was successfully freed + * + * @example + * var Uint8Array = require( './../../../uint8' ); + * + * var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ), { + * 'persist': true + * }); + * // returns + * + * // ... + * + * var out = ArrayIndex.free( idx.id ); + * // returns true + */ + free( id: string ): boolean; + + /** + * Returns the array associated with a provided identifier. + * + * @param id - identifier + * @returns object containing array index data + * + * @example + * var Uint8Array = require( './../../../uint8' ); + * + * var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ), { + * 'persist': true + * }); + * // returns + * + * // ... + * + * var o = ArrayIndex.get( idx.id ); + * // returns {...} + * + * var d = o.data; + * // returns [ 1, 0, 1, 0 ] + * + * var t = o.type; + * // returns 'mask' + * + * var dt = o.dtype; + * // returns 'uint8' + */ + get( id: string ): ArrayObject; +} + +/** +* Array index constructor. +* +* @param x - input array +* @param options - function options +* @param options.persist - boolean indicating whether to continue persisting an index object after first usage +* @returns ArrayIndex instance +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var x = new Uint8Array( [ 1, 0, 1, 0 ] ); +* +* var idx = new ArrayIndex( x ); +* // returns +*/ +declare var ctor: Constructor; + + +// EXPORTS // + +export = ctor; diff --git a/index/docs/types/test.ts b/index/docs/types/test.ts new file mode 100644 index 00000000..d330d1fb --- /dev/null +++ b/index/docs/types/test.ts @@ -0,0 +1,189 @@ +/* +* @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. +*/ + +/* eslint-disable @typescript-eslint/no-unused-expressions */ + +import ArrayIndex = require( './index' ); + + +// TESTS // + +// The function returns an array index... +{ + const x = [ 1, 2, 3, 4 ]; + const y = [ true, false, true, false ]; + const z = new Uint8Array( [ 1, 0, 1, 0 ] ); + const w = new Int32Array( [ 1, 2, 3, 4 ] ); + + new ArrayIndex( x ); // $ExpectType IntegerArrayIndex + new ArrayIndex( y ); // $ExpectType BooleanArrayIndex + new ArrayIndex( z ); // $ExpectType MaskArrayIndex + new ArrayIndex( w ); // $ExpectType Int32ArrayIndex + + new ArrayIndex( x, { 'persist': true } ); // $ExpectType IntegerArrayIndex + new ArrayIndex( y, { 'persist': true } ); // $ExpectType BooleanArrayIndex + new ArrayIndex( z, { 'persist': true } ); // $ExpectType MaskArrayIndex + new ArrayIndex( w, { 'persist': true } ); // $ExpectType Int32ArrayIndex + + ArrayIndex( x ); // $ExpectType IntegerArrayIndex + ArrayIndex( y ); // $ExpectType BooleanArrayIndex + ArrayIndex( z ); // $ExpectType MaskArrayIndex + ArrayIndex( w ); // $ExpectType Int32ArrayIndex + + ArrayIndex( x, { 'persist': true } ); // $ExpectType IntegerArrayIndex + ArrayIndex( y, { 'persist': true } ); // $ExpectType BooleanArrayIndex + ArrayIndex( z, { 'persist': true } ); // $ExpectType MaskArrayIndex + ArrayIndex( w, { 'persist': true } ); // $ExpectType Int32ArrayIndex +} + +// The compiler throws an error if the function is provided first argument which is not a valid collection... +{ + ArrayIndex( 'abc' ); // $ExpectError + ArrayIndex( 1 ); // $ExpectError + ArrayIndex( null ); // $ExpectError + ArrayIndex( void 0 ); // $ExpectError + ArrayIndex( true ); // $ExpectError + ArrayIndex( false ); // $ExpectError + ArrayIndex( {} ); // $ExpectError + ArrayIndex( [ {} ] ); // $ExpectError + ArrayIndex( ( x: number ): number => x ); // $ExpectError + + ArrayIndex( 'abc', {} ); // $ExpectError + ArrayIndex( 1, {} ); // $ExpectError + ArrayIndex( null, {} ); // $ExpectError + ArrayIndex( void 0, {} ); // $ExpectError + ArrayIndex( true, {} ); // $ExpectError + ArrayIndex( false, {} ); // $ExpectError + ArrayIndex( {}, {} ); // $ExpectError + ArrayIndex( [ {} ], {} ); // $ExpectError + ArrayIndex( ( x: number ): number => x, {} ); // $ExpectError +} + +// The compiler throws an error if the function is provided second argument which is not an object... +{ + const x = [ 1, 2, 3, 4 ]; + + ArrayIndex( x, 'abc' ); // $ExpectError + ArrayIndex( x, 1 ); // $ExpectError + ArrayIndex( x, null ); // $ExpectError + ArrayIndex( x, true ); // $ExpectError + ArrayIndex( x, false ); // $ExpectError + ArrayIndex( x, [ {} ] ); // $ExpectError + ArrayIndex( x, ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the function is provided `persist` option which is not a boolean... +{ + const x = [ 1, 2, 3, 4 ]; + + ArrayIndex( x, { 'persist': 'abc' } ); // $ExpectError + ArrayIndex( x, { 'persist': 1 } ); // $ExpectError + ArrayIndex( x, { 'persist': null } ); // $ExpectError + ArrayIndex( x, { 'persist': {} } ); // $ExpectError + ArrayIndex( x, { 'persist': [] } ); // $ExpectError + ArrayIndex( x, { 'persist': ( x: number ): number => x } ); // $ExpectError +} + +// The compiler throws an error if the function is provided an unsupported number of arguments... +{ + const x = [ 1, 2, 3, 4 ]; + + ArrayIndex(); // $ExpectError + ArrayIndex( x, {}, {} ); // $ExpectError +} + +// Attached to the main export is a `free` function which returns a boolean... +{ + ArrayIndex.free( '0' ); // $ExpectType boolean +} + +// The compiler throws an error if the `free` method is provided first argument which is not a string... +{ + ArrayIndex.free( 1 ); // $ExpectError + ArrayIndex.free( null ); // $ExpectError + ArrayIndex.free( void 0 ); // $ExpectError + ArrayIndex.free( true ); // $ExpectError + ArrayIndex.free( false ); // $ExpectError + ArrayIndex.free( {} ); // $ExpectError + ArrayIndex.free( [] ); // $ExpectError + ArrayIndex.free( ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the `free` method is provided an unsupported number of arguments... +{ + ArrayIndex.free(); // $ExpectError + ArrayIndex.free( '0', {} ); // $ExpectError +} + +// Attached to the main export is a `get` function which returns array object data... +{ + ArrayIndex.get( '0' ); // $ExpectType ArrayObject +} + +// The compiler throws an error if the `get` method is provided first argument which is not a string... +{ + ArrayIndex.get( 1 ); // $ExpectError + ArrayIndex.get( null ); // $ExpectError + ArrayIndex.get( void 0 ); // $ExpectError + ArrayIndex.get( true ); // $ExpectError + ArrayIndex.get( false ); // $ExpectError + ArrayIndex.get( {} ); // $ExpectError + ArrayIndex.get( [] ); // $ExpectError + ArrayIndex.get( ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the `get` method is provided an unsupported number of arguments... +{ + ArrayIndex.get(); // $ExpectError + ArrayIndex.get( '0', {} ); // $ExpectError +} + +// An array index has a `dtype` property which returns a string... +{ + const x = new ArrayIndex( [ 1, 2, 3, 4 ] ); + + x.dtype; // $ExpectType "generic" +} + +// An array index has an `id` property which returns a string... +{ + const x = new ArrayIndex( [ 1, 2, 3, 4 ] ); + + x.id; // $ExpectType string +} + +// An array index has a `type` property which returns a string... +{ + const x = new ArrayIndex( [ 1, 2, 3, 4 ] ); + + x.type; // $ExpectType "int" +} + +// An array index has a `data` property which returns a collection... +{ + const x = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); + + x.data; // $ExpectType Uint8Array +} + +// An array index has an `isCached` property which returns a boolean... +{ + const x = new ArrayIndex( [ 1, 2, 3, 4 ] ); + + x.isCached; // $ExpectType boolean +} diff --git a/index/examples/index.js b/index/examples/index.js new file mode 100644 index 00000000..f64908f4 --- /dev/null +++ b/index/examples/index.js @@ -0,0 +1,59 @@ +/** +* @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 Uint8Array = require( './../../uint8' ); +var Int32Array = require( './../../int32' ); +var ArrayIndex = require( './../lib' ); + +var x = new Uint8Array( [ 1, 0, 1, 0 ] ); +var i = new ArrayIndex( x ); +// returns + +var o = ArrayIndex.get( i.id ); +// returns {...} + +console.log( 'Type: %s. Data type: %s.', o.type, o.dtype ); + +x = [ true, false, true, false ]; +i = new ArrayIndex( x ); +// returns + +o = ArrayIndex.get( i.id ); +// returns {...} + +console.log( 'Type: %s. Data type: %s.', o.type, o.dtype ); + +x = new Int32Array( [ 1, 3, 4, 7 ] ); +i = new ArrayIndex( x ); +// returns + +o = ArrayIndex.get( i.id ); +// returns {...} + +console.log( 'Type: %s. Data type: %s.', o.type, o.dtype ); + +x = [ 1, 3, 4, 7 ]; +i = new ArrayIndex( x ); +// returns + +o = ArrayIndex.get( i.id ); +// returns {...} + +console.log( 'Type: %s. Data type: %s.', o.type, o.dtype ); diff --git a/index/lib/cache.js b/index/lib/cache.js new file mode 100644 index 00000000..ff74dc49 --- /dev/null +++ b/index/lib/cache.js @@ -0,0 +1,40 @@ +/** +* @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 LinkedList = require( '@stdlib/utils/linked-list' ); + + +// MAIN // + +/** +* Cache for storing index arrays. +* +* @private +* @name cache +* @type {LinkedList} +*/ +var cache = new LinkedList(); // note: created as a linked list to allow for more efficient removal of expired index arrays + + +// EXPORTS // + +module.exports = cache; diff --git a/index/lib/cache_gc.js b/index/lib/cache_gc.js new file mode 100644 index 00000000..b3160110 --- /dev/null +++ b/index/lib/cache_gc.js @@ -0,0 +1,59 @@ +/** +* @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 cache = require( './cache.js' ); + + +// MAIN // + +/** +* Performs garbage collection on the index cache. +* +* @private +* @returns {Object} garbage collection results +*/ +function gc() { + var node; + var N; + var M; + var v; + + node = cache.first(); + N = cache.length; + while ( node ) { + v = node.value; + if ( !v.persist ) { + cache.remove( node ); + } + node = node.next; + } + M = cache.length; + return { + 'size': M, + 'removed': N - M + }; +} + + +// EXPORTS // + +module.exports = gc; diff --git a/index/lib/defaults.js b/index/lib/defaults.js new file mode 100644 index 00000000..44fa8dc1 --- /dev/null +++ b/index/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} defaults +* +* @example +* var o = defaults(); +* // returns {...} +*/ +function defaults() { + return { + 'persist': false + }; +} + + +// EXPORTS // + +module.exports = defaults; diff --git a/index/lib/find.js b/index/lib/find.js new file mode 100644 index 00000000..3e14813f --- /dev/null +++ b/index/lib/find.js @@ -0,0 +1,49 @@ +/** +* @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 cache = require( './cache.js' ); + + +// MAIN // + +/** +* Returns an array index object associated with a specified identifier. +* +* @private +* @param {*} id - identifier +* @returns {(Node|null)} array index object +*/ +function find( id ) { // eslint-disable-line stdlib/no-redeclare + var node = cache.first(); + while ( node ) { + if ( node.value.id === id ) { + return node; + } + node = node.next; + } + return null; +} + + +// EXPORTS // + +module.exports = find; diff --git a/index/lib/id.js b/index/lib/id.js new file mode 100644 index 00000000..64b99b46 --- /dev/null +++ b/index/lib/id.js @@ -0,0 +1,46 @@ +/** +* @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'; + +// VARIABLES // + +var COUNTER = -1; // TODO: consider another approach for unique identifier generation. For most cases, this should suffice; however, it is possible that two different libraries, both relying on separate copies of this package, may trigger id collisions in the event that instantiated instances were to interact (e.g., a consumer attempting to free an instance instantiated by another copy of the package, etc). + + +// MAIN // + +/** +* Generates a new identifier. +* +* @private +* @returns {string} identifier +* +* @example +* var v = id(); +* // returns +*/ +function id() { + COUNTER += 1; + return COUNTER.toString(); +} + + +// EXPORTS // + +module.exports = id; diff --git a/index/lib/index.js b/index/lib/index.js new file mode 100644 index 00000000..6c5586d8 --- /dev/null +++ b/index/lib/index.js @@ -0,0 +1,43 @@ +/** +* @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'; + +/** +* Array index constructor. +* +* @module @stdlib/array/index +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* var ArrayIndex = require( '@stdlib/array/index' ); +* +* var x = new Uint8Array( [ 1, 0, 1, 0 ] ); +* +* var idx = new ArrayIndex( x ); +* // returns +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/index/lib/main.js b/index/lib/main.js new file mode 100644 index 00000000..73544516 --- /dev/null +++ b/index/lib/main.js @@ -0,0 +1,443 @@ +/** +* @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. +*/ + +/* eslint-disable no-restricted-syntax, no-invalid-this */ + +'use strict'; + +// MODULES // + +var setReadOnlyAccessor = require( '@stdlib/utils/define-nonenumerable-read-only-accessor' ); +var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var setNonEnumerable = require( '@stdlib/utils/define-nonenumerable-property' ); +var isCollection = require( '@stdlib/assert/is-collection' ); +var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; +var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive; +var isAccessorArray = require( './../../base/assert/is-accessor-array' ); +var array2json = require( './../../to-json' ); +var dtype = require( './../../dtype' ); +var copy = require( './../../base/copy' ); +var resolveGetter = require( './../../base/resolve-getter' ); +var format = require( '@stdlib/string/format' ); +var defaults = require( './defaults.js' ); +var validate = require( './validate.js' ); +var cache = require( './cache.js' ); +var findArrayIndex = require( './find.js' ); +var generateId = require( './id.js' ); + + +// MAIN // + +/** +* Array index constructor. +* +* @param {Collection} x - input array +* @param {Options} [options] - function options +* @param {boolean} [options.persist=false] - boolean indicating whether to continue persisting an index object after first usage +* @throws {TypeError} first argument must be an array-like object +* @throws {TypeError} first argument must be a valid index array +* @throws {TypeError} options argument must be an object +* @throws {TypeError} must provide valid options +* @returns {ArrayIndex} ArrayIndex instance +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var x = new Uint8Array( [ 1, 0, 1, 0 ] ); +* +* var idx = new ArrayIndex( x ); +* // returns +*/ +function ArrayIndex( x ) { + var opts; + var err; + var get; + var dt; + var t; + var v; + if ( !(this instanceof ArrayIndex) ) { + if ( arguments.length > 1 ) { + return new ArrayIndex( x, arguments[ 1 ] ); + } + return new ArrayIndex( x ); + } + if ( !isCollection( x ) ) { + throw new TypeError( format( 'invalid argument. First argument must be an array-like object. Value: `%s`.', x ) ); + } + dt = dtype( x ); + + // When provided a "generic" array or an array of an unknown data type, attempt to infer the type of index array... + if ( dt === 'generic' || dt === null ) { + get = resolveGetter( x ); + v = get( x, 0 ); + + // Infer the "type" of index array from the first element... + if ( isBoolean( v ) ) { + t = 'bool'; + } else if ( isInteger( v ) ) { + t = 'int'; + } else { + throw new TypeError( 'invalid argument. First argument must be a valid index array.' ); + } + } else if ( dt === 'int32' ) { + t = 'int'; + } else if ( dt === 'uint8' ) { + t = 'mask'; + } else { + throw new TypeError( 'invalid argument. First argument must be a valid index array.' ); + } + // Resolve index options: + opts = defaults(); + if ( arguments.length > 1 ) { + err = validate( opts, arguments[ 1 ] ); + if ( err ) { + throw err; + } + } + // Add the array index to the index cache: + cache.push({ + 'id': generateId(), + 'ref': this, + 'data': x, + 'type': t, + 'dtype': dt, + 'persist': opts.persist + }); + + // Store a reference to the cache node: + setReadOnly( this, '_node', cache.last() ); + + // Initialize a boolean flag indicating whether an array index object has been invalidated (i.e., freed): + setNonEnumerable( this, '_invalidated', false ); + + return this; +} + +/** +* Constructor name. +* +* @name name +* @memberof ArrayIndex +* @readonly +* @type {string} +* @default 'ArrayIndex' +* +* @example +* var str = ArrayIndex.name; +* // returns 'ArrayIndex' +*/ +setReadOnly( ArrayIndex, 'name', 'ArrayIndex' ); + +/** +* Frees an array index object associated with a provided identifier. +* +* @name free +* @memberof ArrayIndex +* @type {Function} +* @param {string} id - identifier +* @returns {boolean} boolean indicating whether an array index object was successfully freed +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ), { +* 'persist': true +* }); +* // returns +* +* // ... +* +* var out = ArrayIndex.free( idx.id ); +* // returns true +*/ +setReadOnly( ArrayIndex, 'free', function free( id ) { + var node; + var v; + + // Retrieve the array index object with the specified identifier: + node = findArrayIndex( id ); + if ( node === null ) { + return false; + } + v = node.value; + + // Invalidate the array instance object: + setReadOnly( v.ref, '_invalidated', true ); + + // Remove the array instance from the cache: + cache.remove( node ); + + // Remove the reference to the original array: + v.data = null; + + return true; +}); + +/** +* Returns the array associated with a provided identifier. +* +* @name get +* @memberof ArrayIndex +* @type {Function} +* @param {string} id - identifier +* @returns {(Object|null)} object containing array index data +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ), { +* 'persist': true +* }); +* // returns +* +* // ... +* +* var o = ArrayIndex.get( idx.id ); +* // returns {...} +* +* var d = o.data; +* // returns [ 1, 0, 1, 0 ] +* +* var t = o.type; +* // returns 'mask' +* +* var dt = o.dtype; +* // returns 'uint8' +*/ +setReadOnly( ArrayIndex, 'get', function get( id ) { + var node; + var out; + var v; + + // Retrieve the array index object with the specified identifier: + node = findArrayIndex( id ); + if ( node === null ) { + return null; + } + v = node.value; + + // Assemble the output object: + out = { + 'data': v.data, + 'type': v.type, + 'dtype': v.dtype + }; + + // If the array index object should not be persisted, go ahead and remove the object from the cache... + if ( !v.persist ) { + ArrayIndex.free( id ); // note: this should come last, after having retrieved all desired array index node data + } + return out; +}); + +/** +* Returns the underlying array data of array index object. +* +* @name data +* @memberof ArrayIndex.prototype +* @readonly +* @type {Collection} +* @throws {Error} array index is no longer valid +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +* // returns +* +* var v = idx.data; +* // returns [ 1, 0, 1, 0 ] +*/ +setReadOnlyAccessor( ArrayIndex.prototype, 'data', function get() { + if ( this._invalidated ) { + throw new Error( 'invalid operation. This array index instance has already been freed and can no longer be used.' ); + } + return this._node.value.data; +}); + +/** +* Returns the underlying array data type of array index object. +* +* @name dtype +* @memberof ArrayIndex.prototype +* @readonly +* @type {string} +* @throws {Error} array index is no longer valid +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +* // returns +* +* var t = idx.dtype; +* // returns 'uint8' +*/ +setReadOnlyAccessor( ArrayIndex.prototype, 'dtype', function get() { + if ( this._invalidated ) { + throw new Error( 'invalid operation. This array index instance has already been freed and can no longer be used.' ); + } + return this._node.value.dtype; +}); + +/** +* Returns the identifier associated with an array index object. +* +* @name id +* @memberof ArrayIndex.prototype +* @readonly +* @type {string} +* @throws {Error} array index is no longer valid +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +* // returns +* +* var id = idx.id; +* // returns +*/ +setReadOnlyAccessor( ArrayIndex.prototype, 'id', function get() { + if ( this._invalidated ) { + throw new Error( 'invalid operation. This array index instance has already been freed and can no longer be used.' ); + } + return this._node.value.id; +}); + +/** +* Returns a boolean indicating if an array index is actively cached. +* +* @name isCached +* @memberof ArrayIndex.prototype +* @readonly +* @type {boolean} +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +* // returns +* +* var out = idx.isCached; +* // returns true +*/ +setReadOnlyAccessor( ArrayIndex.prototype, 'isCached', function get() { + return !this._invalidated; +}); + +/** +* Returns the type of array index object. +* +* @name type +* @memberof ArrayIndex.prototype +* @readonly +* @type {string} +* @throws {Error} array index is no longer valid +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +* // returns +* +* var t = idx.type; +* // returns 'mask' +*/ +setReadOnlyAccessor( ArrayIndex.prototype, 'type', function get() { + if ( this._invalidated ) { + throw new Error( 'invalid operation. This array index instance has already been freed and can no longer be used.' ); + } + return this._node.value.type; +}); + +/** +* Serializes an array index object to a string. +* +* @name toString +* @memberof ArrayIndex.prototype +* @type {Function} +* @throws {Error} array index is no longer valid +* @returns {string} serialized array index object +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +* // returns +* +* var str = idx.toString(); +* // e.g., 'ArrayIndex<0>' +*/ +setReadOnly( ArrayIndex.prototype, 'toString', function toString() { + var v; + if ( this._invalidated ) { + throw new Error( 'invalid operation. This array index instance has already been freed and can no longer be used.' ); + } + v = this._node.value; + return 'ArrayIndex<' + v.id + '>'; +}); + +/** +* Serializes an array index object as a JSON object. +* +* ## Notes +* +* - `JSON.stringify()` implicitly calls this method when stringifying an `ArrayIndex` instance. +* +* @name toJSON +* @memberof ArrayIndex.prototype +* @type {Function} +* @throws {Error} array index is no longer valid +* @returns {Object} serialized array index object +* +* @example +* var Uint8Array = require( '@stdlib/array/uint8' ); +* +* var idx = new ArrayIndex( new Uint8Array( [ 1, 0, 1, 0 ] ) ); +* // returns +* +* var o = idx.toJSON(); +* // returns { 'type': 'ArrayIndex', 'data': { 'type': 'Uint8Array', 'data': [ 1, 0, 1, 0 ] } } +*/ +setReadOnly( ArrayIndex.prototype, 'toJSON', function toJSON() { + var v; + var o; + if ( this._invalidated ) { + throw new Error( 'invalid operation. This array index instance has already been freed and can no longer be used.' ); + } + v = this._node.value; + if ( v.dtype === 'generic' || v.dtype === null ) { + if ( isAccessorArray( v.data ) ) { + o = copy( v.data ); + } else { + o = v.data; + } + } else { + o = array2json( v.data ); + } + return { + 'type': 'ArrayIndex', + 'data': o + }; +}); + + +// EXPORTS // + +module.exports = ArrayIndex; diff --git a/index/lib/validate.js b/index/lib/validate.js new file mode 100644 index 00000000..75d3a46a --- /dev/null +++ b/index/lib/validate.js @@ -0,0 +1,66 @@ +/** +* @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 isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; +var format = require( '@stdlib/string/format' ); + + +// MAIN // + +/** +* Validates function options. +* +* @private +* @param {Object} opts - destination object +* @param {Options} options - function options +* @param {boolean} [options.persist] - boolean indicating whether to continue persisting an index object after first usage +* @returns {(Error|null)} null or an error object +* +* @example +* var opts = {}; +* var options = { +* 'persist': false +* }; +* 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, 'persist' ) ) { + opts.persist = options.persist; + if ( !isBoolean( opts.persist ) ) { + return new TypeError( format( 'invalid option. `%s` option must be a boolean. Option: `%s`.', 'persist', opts.persist ) ); + } + } + return null; +} + + +// EXPORTS // + +module.exports = validate; diff --git a/index/package.json b/index/package.json new file mode 100644 index 00000000..e3241bb6 --- /dev/null +++ b/index/package.json @@ -0,0 +1,66 @@ +{ + "name": "@stdlib/array/index", + "version": "0.0.0", + "description": "Array index constructor.", + "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", + "stdtypes", + "types", + "data", + "structure", + "array", + "fancy", + "indexing", + "index", + "vector", + "slice", + "constructor", + "ctor" + ] +} diff --git a/index/test/test.js b/index/test/test.js new file mode 100644 index 00000000..dd99b3dd --- /dev/null +++ b/index/test/test.js @@ -0,0 +1,87 @@ +/** +* @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 instanceOf = require( '@stdlib/assert/instance-of' ); +var Uint8Array = require( './../../uint8' ); +var Int32Array = require( './../../int32' ); +var toAccessorArray = require( './../../base/to-accessor-array' ); +var ArrayIndex = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof ArrayIndex, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function is a constructor', function test( t ) { + var x; + + x = new ArrayIndex( [ 1, 2, 3 ] ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + x = new ArrayIndex( [ true, false, true ] ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + x = new ArrayIndex( toAccessorArray( [ 1, 2, 3 ] ) ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + x = new ArrayIndex( new Uint8Array( [ 1, 0, 1 ] ) ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + x = new ArrayIndex( new Int32Array( [ 1, 0, 1 ] ) ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function does not require the `new` keyword', function test( t ) { + var idx; + var x; + + idx = ArrayIndex; + + x = idx( [ 1, 2, 3 ] ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + x = idx( [ true, false, true ] ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + x = idx( toAccessorArray( [ 1, 2, 3 ] ) ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + x = idx( new Uint8Array( [ 1, 0, 1 ] ) ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + x = idx( new Int32Array( [ 1, 0, 1 ] ) ); + t.strictEqual( instanceOf( x, ArrayIndex ), true, 'returns expected value' ); + + t.end(); +}); + +tape( 'attached to the constructor is a `name` property', function test( t ) { + t.strictEqual( ArrayIndex.name, 'ArrayIndex', 'returns expected value' ); + t.end(); +}); diff --git a/lib/index.js b/lib/index.js index a12c3393..03af8058 100644 --- a/lib/index.js +++ b/lib/index.js @@ -274,6 +274,15 @@ setReadOnly( ns, 'afullLike', require( './../full-like' ) ); */ setReadOnly( ns, 'incrspace', require( './../incrspace' ) ); +/** +* @name ArrayIndex +* @memberof ns +* @readonly +* @constructor +* @see {@link module:@stdlib/array/index} +*/ +setReadOnly( ns, 'ArrayIndex', require( './../index' ) ); + /** * @name Int8Array * @memberof ns @@ -472,6 +481,15 @@ setReadOnly( ns, 'SharedArrayBuffer', require( './../shared-buffer' ) ); */ setReadOnly( ns, 'aslice', require( './../slice' ) ); +/** +* @name atake +* @memberof ns +* @readonly +* @type {Function} +* @see {@link module:@stdlib/array/take} +*/ +setReadOnly( ns, 'atake', require( './../take' ) ); + /** * @name circarray2iterator * @memberof ns diff --git a/one-to/README.md b/one-to/README.md index 0c995839..a767c284 100644 --- a/one-to/README.md +++ b/one-to/README.md @@ -142,6 +142,15 @@ console.log( y ); @@ -152,6 +161,18 @@ console.log( y ); [mdn-typed-array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray + + +[@stdlib/array/full]: https://github.com/stdlib-js/array/tree/main/full + +[@stdlib/array/ones]: https://github.com/stdlib-js/array/tree/main/ones + +[@stdlib/array/one-to-like]: https://github.com/stdlib-js/array/tree/main/one-to-like + +[@stdlib/array/zero-to]: https://github.com/stdlib-js/array/tree/main/zero-to + + + diff --git a/package.json b/package.json index 76fa77ec..4a191f53 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@stdlib/iter": "github:stdlib-js/iter#main", "@stdlib/math": "github:stdlib-js/math#main", "@stdlib/ndarray": "github:stdlib-js/ndarray#main", + "@stdlib/object": "github:stdlib-js/object#main", "@stdlib/proxy": "github:stdlib-js/proxy#main", "@stdlib/slice": "github:stdlib-js/slice#main", "@stdlib/strided": "github:stdlib-js/strided#main", diff --git a/take/README.md b/take/README.md new file mode 100644 index 00000000..dcf22b2a --- /dev/null +++ b/take/README.md @@ -0,0 +1,155 @@ + + +# take + +> Take elements from an array. + +
+ +## Usage + +```javascript +var take = require( '@stdlib/array/take' ); +``` + +#### take( x, indices\[, options] ) + +Takes elements from an array. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var y = take( x, [ 1, 3 ] ); +// returns [ 2, 4 ] +``` + +The function supports the following parameters: + +- **x**: input array. +- **indices**: list of indices. +- **options**: function options. + +The function supports the following options: + +- **mode**: index [mode][@stdlib/ndarray/base/ind]. Default: `'normalize'`. + +By default, the function normalizes negative integer indices to positive integer index equivalents. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var y = take( x, [ -3, -1 ] ); +// returns [ 2, 4 ] +``` + +To specify an alternative index [mode][@stdlib/ndarray/base/ind], provide a `mode` option. + +```javascript +var x = [ 1, 2, 3, 4 ]; + +var y = take( x, [ -10, 10 ], { + 'mode': 'clamp' +}); +// returns [ 1, 4 ] +``` + +
+ + + +
+ +## Notes + +- If `indices` is an empty array, the function returns an empty array. + + ```javascript + var x = [ 1, 2, 3, 4 ]; + + var y = take( x, [] ); + // returns [] + ``` + +- If provided an input array having a recognized [data type][@stdlib/array/dtypes], the function returns an array having the same [data type][@stdlib/array/dtypes] as the input array. Otherwise, the function **always** returns a "generic" array. + +
+ + + +
+ +## Examples + + + +```javascript +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var linspace = require( '@stdlib/array/linspace' ); +var take = require( '@stdlib/array/take' ); + +// Generate a linearly spaced array: +var x = linspace( 0, 100, 11 ); +console.log( x ); + +// Generate an array of random indices: +var indices = discreteUniform( 10, 0, x.length-1 ); +console.log( indices ); + +// Take a random sample of elements from `x`: +var y = take( x, indices ); +console.log( y ); +``` + +
+ + + + + + + + + + + + + + diff --git a/take/benchmark/benchmark.js b/take/benchmark/benchmark.js new file mode 100644 index 00000000..8975c75d --- /dev/null +++ b/take/benchmark/benchmark.js @@ -0,0 +1,52 @@ +/** +* @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 pkg = require( './../package.json' ).name; +var take = require( './../lib' ); + + +// MAIN // + +bench( pkg+'::copy:len=100', function benchmark( b ) { + var x; + var i; + var v; + + x = zeroTo( 100 ); + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = take( x, 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(); +}); diff --git a/take/benchmark/benchmark.length.js b/take/benchmark/benchmark.length.js new file mode 100644 index 00000000..f81872fd --- /dev/null +++ b/take/benchmark/benchmark.length.js @@ -0,0 +1,98 @@ +/** +* @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 isArray = require( '@stdlib/assert/is-array' ); +var pkg = require( './../package.json' ).name; +var take = require( './../lib' ); + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} len - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( len ) { + var idx = discreteUniform( len, 0, 3 ); + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var x; + var v; + var i; + + x = [ 1, 2, 3, 4 ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = take( x, idx ); + 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/take/docs/repl.txt b/take/docs/repl.txt new file mode 100644 index 00000000..eb4e87aa --- /dev/null +++ b/take/docs/repl.txt @@ -0,0 +1,40 @@ + +{{alias}}( x, indices[, options] ) + Takes elements from an array. + + If `indices` is an empty array, the function returns an empty array. + + Parameters + ---------- + x: Array|TypedArray|Object + Input array. + + indices: ArrayLikeObject + List of element indices. + + options: Object (optional) + Function options. + + options.mode: string (optional) + Specifies how to handle an index outside the interval [0, max], where + `max` is the maximum possible array index. If equal to 'throw', the + function throws an error. If equal to 'normalize', the function throws + an error if provided an out-of-bounds normalized index. If equal to + 'wrap', the function wraps around an index using modulo arithmetic. If + equal to 'clamp', the function sets an index to either 0 (minimum index) + or the maximum index. Default: 'normalize'. + + Returns + ------- + out: Array|TypedArray + Output array. + + Examples + -------- + > var x = [ 1, 2, 3, 4 ]; + > var y = {{alias}}( x, [ 1, 3 ] ) + [ 2, 4 ] + + See Also + -------- + diff --git a/take/docs/types/index.d.ts b/take/docs/types/index.d.ts new file mode 100644 index 00000000..fcf1547f --- /dev/null +++ b/take/docs/types/index.d.ts @@ -0,0 +1,247 @@ +/* +* @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, Complex128Array, Complex64Array } from '@stdlib/types/array'; +import { Mode } from '@stdlib/types/ndarray'; + +/** +* Index array. +*/ +type IndexArray = Collection | AccessorArrayLike; + +/** +* Interface describing function options. +*/ +interface Options { + /** + * Index mode. + */ + mode?: Mode; +} + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'float64' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2.0, 4.0 ] +*/ +declare function take( x: Float64Array, indices: IndexArray, options?: Options ): Float64Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'float32' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2.0, 4.0 ] +*/ +declare function take( x: Float32Array, indices: IndexArray, options?: Options ): Float32Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'int32' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2, 4 ] +*/ +declare function take( x: Int32Array, indices: IndexArray, options?: Options ): Int32Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'int16' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2, 4 ] +*/ +declare function take( x: Int16Array, indices: IndexArray, options?: Options ): Int16Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'int8' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2, 4 ] +*/ +declare function take( x: Int8Array, indices: IndexArray, options?: Options ): Int8Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'uint32' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2, 4 ] +*/ +declare function take( x: Uint32Array, indices: IndexArray, options?: Options ): Uint32Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'uint16' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2, 4 ] +*/ +declare function take( x: Uint16Array, indices: IndexArray, options?: Options ): Uint16Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'uint8' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2, 4 ] +*/ +declare function take( x: Uint8Array, indices: IndexArray, options?: Options ): Uint8Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'uint8c' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2, 4 ] +*/ +declare function take( x: Uint8ClampedArray, indices: IndexArray, options?: Options ): Uint8ClampedArray; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'complex128' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2.0, 0.0, 4.0, 0.0 ] +*/ +declare function take( x: Complex128Array, indices: IndexArray, options?: Options ): Complex128Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var zeroTo = require( '@stdlib/array/zero-to' ); +* +* var x = zeroTo( 4, 'complex64' ); +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2.0, 0.0, 4.0, 0.0 ] +*/ +declare function take( x: Complex64Array, indices: IndexArray, options?: Options ): Complex64Array; + +/** +* Takes elements from an array. +* +* @param x - input array +* @param indices - list of element indices +* @param options - function options +* @returns output array +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* +* var y = take( x, [ 1, 3 ] ); +* // returns [ 2, 4 ] +*/ +declare function take( x: Collection | AccessorArrayLike, indices: IndexArray, options?: Options ): Array; + + +// EXPORTS // + +export = take; diff --git a/take/docs/types/test.ts b/take/docs/types/test.ts new file mode 100644 index 00000000..0fe1ea80 --- /dev/null +++ b/take/docs/types/test.ts @@ -0,0 +1,94 @@ +/* +* @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 take = require( './index' ); + + +// TESTS // + +// The function returns an array... +{ + take( [ 1, 2, 3, 4 ], [ 1, 3 ] ); // $ExpectType number[] + take( [ 1, 2, 3, 4 ], [ 1, 3 ] ); // $ExpectType any[] + take( [ 1, 2, 3, 4 ], [ 1, 3 ] ); // $ExpectType number[] + take( [ '1', '2', '3', '4' ], [ 1, 3 ] ); // $ExpectType string[] + + take( new Float64Array( 10 ), [ 1, 3 ] ); // $ExpectType Float64Array + take( new Float32Array( 10 ), [ 1, 3 ] ); // $ExpectType Float32Array + take( new Int32Array( 10 ), [ 1, 3 ] ); // $ExpectType Int32Array + take( new Int16Array( 10 ), [ 1, 3 ] ); // $ExpectType Int16Array + take( new Int8Array( 10 ), [ 1, 3 ] ); // $ExpectType Int8Array + take( new Uint32Array( 10 ), [ 1, 3 ] ); // $ExpectType Uint32Array + take( new Uint16Array( 10 ), [ 1, 3 ] ); // $ExpectType Uint16Array + take( new Uint8Array( 10 ), [ 1, 3 ] ); // $ExpectType Uint8Array + take( new Uint8ClampedArray( 10 ), [ 1, 3 ] ); // $ExpectType Uint8ClampedArray + take( new Complex128Array( 10 ), [ 1, 3 ] ); // $ExpectType Complex128Array + take( new Complex64Array( 10 ), [ 1, 3 ] ); // $ExpectType Complex64Array +} + +// The compiler throws an error if the function is provided a first argument which is not an array-like object... +{ + take( 1, [ 1, 3 ] ); // $ExpectError + take( true, [ 1, 3 ] ); // $ExpectError + take( false, [ 1, 3 ] ); // $ExpectError + take( null, [ 1, 3 ] ); // $ExpectError + take( void 0, [ 1, 3 ] ); // $ExpectError + take( {}, [ 1, 3 ] ); // $ExpectError +} + +// The compiler throws an error if the function is provided a second argument which is not an array-like object containing numbers... +{ + take( [], 1 ); // $ExpectError + take( [], true ); // $ExpectError + take( [], false ); // $ExpectError + take( [], null ); // $ExpectError + take( [], void 0 ); // $ExpectError + take( [], {} ); // $ExpectError +} + +// The compiler throws an error if the function is provided a third argument which is not an object... +{ + take( [], [ 1, 3 ], '1' ); // $ExpectError + take( [], [ 1, 3 ], 1 ); // $ExpectError + take( [], [ 1, 3 ], true ); // $ExpectError + take( [], [ 1, 3 ], false ); // $ExpectError + take( [], [ 1, 3 ], null ); // $ExpectError + take( [], [ 1, 3 ], [] ); // $ExpectError + take( [], [ 1, 3 ], ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the function is provided a `mode` option which is not a valid index mode... +{ + take( [], [ 1, 3 ], { 'mode': '1' } ); // $ExpectError + take( [], [ 1, 3 ], { 'mode': 1 } ); // $ExpectError + take( [], [ 1, 3 ], { 'mode': true } ); // $ExpectError + take( [], [ 1, 3 ], { 'mode': false } ); // $ExpectError + take( [], [ 1, 3 ], { 'mode': null } ); // $ExpectError + take( [], [ 1, 3 ], { 'mode': {} } ); // $ExpectError + take( [], [ 1, 3 ], { 'mode': [] } ); // $ExpectError + take( [], [ 1, 3 ], { 'mode': ( x: number ): number => x } ); // $ExpectError +} + +// The compiler throws an error if the function is provided an unsupported number of arguments... +{ + take(); // $ExpectError + take( [] ); // $ExpectError + take( [], [], {}, {} ); // $ExpectError +} diff --git a/take/examples/index.js b/take/examples/index.js new file mode 100644 index 00000000..49fd565a --- /dev/null +++ b/take/examples/index.js @@ -0,0 +1,35 @@ +/** +* @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 discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var linspace = require( './../../linspace' ); +var take = require( './../lib' ); + +// Generate a linearly spaced array: +var x = linspace( 0, 100, 11 ); +console.log( x ); + +// Generate an array of random indices: +var indices = discreteUniform( 10, 0, x.length-1 ); +console.log( indices ); + +// Take a random sample of elements from `x`: +var y = take( x, indices ); +console.log( y ); diff --git a/take/lib/defaults.js b/take/lib/defaults.js new file mode 100644 index 00000000..ea262044 --- /dev/null +++ b/take/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': 'normalize' + }; +} + + +// EXPORTS // + +module.exports = defaults; diff --git a/take/lib/index.js b/take/lib/index.js new file mode 100644 index 00000000..ae09036b --- /dev/null +++ b/take/lib/index.js @@ -0,0 +1,43 @@ +/** +* @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'; + +/** +* Take elements from an array. +* +* @module @stdlib/array/take +* +* @example +* var take = require( '@stdlib/array/take' ); +* +* var x = [ 1, 2, 3, 4 ]; +* +* var indices = [ 0, 0, 1, 1, 3, 3 ]; +* var y = take( x, indices ); +* // returns [ 1, 1, 2, 2, 4, 4 ] +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/take/lib/main.js b/take/lib/main.js new file mode 100644 index 00000000..0ebfff78 --- /dev/null +++ b/take/lib/main.js @@ -0,0 +1,81 @@ +/** +* @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 isCollection = require( '@stdlib/assert/is-collection' ); +var base = require( './../../base/take' ); +var zeros = require( './../../zeros' ); +var dtype = require( './../../dtype' ); +var format = require( '@stdlib/string/format' ); +var defaults = require( './defaults.js' ); +var validate = require( './validate.js' ); + + +// MAIN // + +/** +* Takes elements from an array. +* +* @param {Collection} x - input array +* @param {IntegerArray} indices - list of indices +* @param {Options} [options] - function options +* @param {string} [options.mode='normalize'] - index mode +* @throws {TypeError} first argument must be a collection +* @throws {TypeError} second argument must be a collection +* @throws {TypeError} options argument must be an object +* @throws {Error} must provide valid options +* @returns {Collection} output array +* +* @example +* var x = [ 1, 2, 3, 4 ]; +* var indices = [ 3, 1, 2, 0 ]; +* +* var y = take( x, indices ); +* // returns [ 4, 2, 3, 1 ] +*/ +function take( x, indices ) { + var opts; + var err; + var dt; + if ( !isCollection( x ) ) { + throw new TypeError( format( 'invalid argument. First argument must be an array-like object. Value: `%s`.', x ) ); + } + if ( !isCollection( indices ) ) { + throw new TypeError( format( 'invalid argument. Second argument must be an array-like object. Value: `%s`.', indices ) ); + } + opts = defaults(); + if ( arguments.length > 2 ) { + err = validate( opts, arguments[ 2 ] ); + if ( err ) { + throw err; + } + } + dt = dtype( x ); + if ( dt === 'generic' || dt === null ) { + return base( x, indices, opts.mode ); + } + return base.assign( x, indices, opts.mode, zeros( indices.length, dt ), 1, 0 ); // eslint-disable-line max-len +} + + +// EXPORTS // + +module.exports = take; diff --git a/take/lib/validate.js b/take/lib/validate.js new file mode 100644 index 00000000..24e7153e --- /dev/null +++ b/take/lib/validate.js @@ -0,0 +1,67 @@ +/** +* @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 isIndexMode = require( '@stdlib/ndarray/base/assert/is-index-mode' ); +var modes = require( '@stdlib/ndarray/index-modes' ); +var format = require( '@stdlib/string/format' ); + + +// MAIN // + +/** +* Validates function options. +* +* @private +* @param {Object} opts - destination object +* @param {Options} options - function options +* @param {string} [options.mode] - index mode +* @returns {(Error|null)} null or an error object +* +* @example +* var opts = {}; +* var options = { +* 'mode': 'normalize' +* }; +* 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 ( !isIndexMode( 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/take/package.json b/take/package.json new file mode 100644 index 00000000..208f4f03 --- /dev/null +++ b/take/package.json @@ -0,0 +1,62 @@ +{ + "name": "@stdlib/array/take", + "version": "0.0.0", + "description": "Take elements from an 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", + "take", + "extract", + "copy", + "index" + ] +} diff --git a/take/test/test.js b/take/test/test.js new file mode 100644 index 00000000..0c6e453c --- /dev/null +++ b/take/test/test.js @@ -0,0 +1,419 @@ +/** +* @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 toAccessorArray = require( './../../base/to-accessor-array' ); +var isSameComplex64Array = require( '@stdlib/assert/is-same-complex64array' ); +var isArray = require( '@stdlib/assert/is-array' ); +var take = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof take, '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() { + take( 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() { + take( 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() { + take( [], 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() { + take( [], 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() { + take( [], [], value ); + }; + } +}); + +tape( 'the function throws an error if provided a `mode` option which is not a valid index 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() { + take( [], [], { + 'mode': value + }); + }; + } +}); + +tape( 'the function takes elements from an array (generic)', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = [ 1, 2, 3, 4 ]; + + indices = [ 1, 3 ]; + actual = take( x, indices ); + + expected = [ 2, 4 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 1, 1, 3, 3 ]; + actual = take( x, indices ); + + expected = [ 2, 2, 4, 4 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 3, 2, 1, 0 ]; + actual = take( x, indices ); + + expected = [ 4, 3, 2, 1 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]; + actual = take( x, indices ); + + expected = [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function takes elements from an array (accessor)', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = toAccessorArray( [ 1, 2, 3, 4 ] ); + + indices = toAccessorArray( [ 1, 3 ] ); + actual = take( x, indices ); + + expected = [ 2, 4 ]; + t.strictEqual( isArray( actual ), true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = toAccessorArray( [ 1, 1, 3, 3 ] ); + actual = take( x, indices ); + + expected = [ 2, 2, 4, 4 ]; + t.strictEqual( isArray( actual ), true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = toAccessorArray( [ 3, 2, 1, 0 ] ); + actual = take( x, indices ); + + expected = [ 4, 3, 2, 1 ]; + t.strictEqual( isArray( actual ), true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + indices = toAccessorArray( [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ); + actual = take( x, indices ); + + expected = [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]; + t.strictEqual( isArray( actual ), true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function takes elements from an array (complex typed)', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = new Complex64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] ); + + indices = toAccessorArray( [ 1, 1, 3 ] ); + actual = take( x, indices ); + + expected = new Complex64Array( [ 3.0, 4.0, 3.0, 4.0, 7.0, 8.0 ] ); + t.notEqual( actual, x, 'returns different reference' ); + t.strictEqual( isSameComplex64Array( actual, expected ), true, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function returns an empty array if provided a second argument which is empty', function test( t ) { + var x = [ 1, 2, 3, 4 ]; + t.deepEqual( take( x, [] ), [], 'returns expected value' ); + t.end(); +}); + +tape( 'by default, the function normalizes negative indices', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = [ 1, 2, 3, 4 ]; + + indices = [ -1, -2, -3, -4 ]; + actual = take( x, indices ); + expected = [ 4, 3, 2, 1 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'by default, the function throws an error if provided an out-of-bounds index', function test( t ) { + var indices; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ 2, 50, 1, 2 ]; + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices ); + } +}); + +tape( 'when the "mode" is "throw", the function throws an error if provided an out-of-bounds index', function test( t ) { + var indices; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ 4, 5, 1, 2 ]; + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, { + 'mode': 'throw' + }); + } +}); + +tape( 'when the "mode" is "normalize", the function normalizes negative indices', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = [ 1, 2, 3, 4 ]; + + indices = [ -1, -2, -3, -4 ]; + actual = take( x, indices, { + 'mode': 'normalize' + }); + expected = [ 4, 3, 2, 1 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "normalize", the function throws an error if provided an out-of-bounds index', function test( t ) { + var indices; + var x; + + x = [ 1, 2, 3, 4 ]; + indices = [ 2, 50, 1, 2 ]; + + t.throws( badValue, RangeError, 'throws an error' ); + t.end(); + + function badValue() { + take( x, indices, { + 'mode': 'normalize' + }); + } +}); + +tape( 'when the "mode" is "clamp", the function clamps out-of-bounds indices', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = [ 1, 2, 3, 4 ]; + + indices = [ -10, 10, -5, 5 ]; + actual = take( x, indices, { + 'mode': 'clamp' + }); + expected = [ 1, 4, 1, 4 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'when the "mode" is "wrap", the function wraps out-of-bounds indices', function test( t ) { + var expected; + var indices; + var actual; + var x; + + x = [ 1, 2, 3, 4 ]; + + indices = [ -10, 10, -5, 5 ]; + actual = take( x, indices, { + 'mode': 'wrap' + }); + expected = [ 3, 3, 4, 2 ]; + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); diff --git a/take/test/test.validate.js b/take/test/test.validate.js new file mode 100644 index 00000000..5001a5e6 --- /dev/null +++ b/take/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': 'throw' + }; + opts = {}; + + expected = { + 'mode': 'throw' + }; + + 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(); +}); diff --git a/to-fancy/README.md b/to-fancy/README.md index ed225622..33fe8605 100644 --- a/to-fancy/README.md +++ b/to-fancy/README.md @@ -114,6 +114,7 @@ v = y[ ':' ]; The function supports the following options: - **strict**: boolean indicating whether to enforce strict bounds checking. Default: `false`. +- **cache**: cache for resolving array index objects. Default: [`ArrayIndex`][@stdlib/array/index]. By default, the function returns a fancy array which does **not** enforce strict bounds checking. For example, @@ -137,16 +138,64 @@ var v = y[ 10 ]; // throws ``` +#### array2fancy.factory( \[options] ) + +Returns a function for converting an array to an object supporting fancy indexing. + +```javascript +var fcn = array2fancy.factory(); + +var x = [ 1, 2, 3, 4 ]; + +var y = fcn( x ); +// returns + +var v = y[ ':' ]; +// returns [ 1, 2, 3, 4 ] +``` + +The function supports the following options: + +- **strict**: boolean indicating whether to enforce strict bounds checking by default. Default: `false`. +- **cache**: cache for resolving array index objects. Default: [`ArrayIndex`][@stdlib/array/index]. + +By default, the function returns a function which, by default, does **not** enforce strict bounds checking. For example, + +```javascript +var fcn = array2fancy.factory(); + +var y = fcn( [ 1, 2, 3, 4 ] ); + +var v = y[ 10 ]; +// returns undefined +``` + +To enforce strict bounds checking by default, set the `strict` option to `true`. + + + +```javascript +var fcn = array2fancy.factory({ + 'strict': true +}); +var y = fcn( [ 1, 2, 3, 4 ] ); + +var v = y[ 10 ]; +// throws +``` + +The returned function supports the same options as above. When the returned function is provided option values, those values override the factory method defaults. + -
- * * * +
+ ## Notes - A fancy array shares the **same** data as the provided input array. Hence, any mutations to the returned array will affect the underlying input array and vice versa. @@ -313,10 +362,10 @@ y[ ':' ] = new Float64Array( [ 5.0, 6.0 ] ); // is this a single complex number -
- * * * +
+ ## Examples @@ -388,6 +437,8 @@ z = y[ ':' ]; [@stdlib/array/complex64]: https://github.com/stdlib-js/array/tree/main/complex64 +[@stdlib/array/index]: https://github.com/stdlib-js/array/tree/main/index +
diff --git a/to-fancy/benchmark/benchmark.factory.js b/to-fancy/benchmark/benchmark.factory.js new file mode 100644 index 00000000..d1dd1a91 --- /dev/null +++ b/to-fancy/benchmark/benchmark.factory.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'; + +// MODULES // + +var bench = require( '@stdlib/bench' ); +var isFunction = require( '@stdlib/assert/is-function' ); +var pkg = require( './../package.json' ).name; +var array2fancy = require( './../lib' ); + + +// MAIN // + +bench( pkg+':factory', function benchmark( b ) { + var v; + var i; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + v = array2fancy.factory(); + if ( typeof v !== 'function' ) { + b.fail( 'should return a function' ); + } + } + b.toc(); + if ( !isFunction( v ) ) { + b.fail( 'should return a function' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/to-fancy/docs/repl.txt b/to-fancy/docs/repl.txt index a4ed2de2..1481081f 100644 --- a/to-fancy/docs/repl.txt +++ b/to-fancy/docs/repl.txt @@ -69,6 +69,19 @@ Boolean indicating whether to enforce strict bounds checking. Default: false. + options.cache: Object (optional) + Cache for resolving array index objects. Must have a 'get' method which + accepts a single argument: an identifier associated with an array index. + If an array index associated with a provided identifier exists, the + 'get' method should return an object having the following properties: + + - data: the underlying index array. + - type: the index type. Must be either 'mask', 'bool', or 'int'. + - dtype: the data type of the underlying array. + + If an array index is not associated with a provided identifier, the + 'get' method should return `null`. + Returns ------- out: Array|TypedArray|Object @@ -82,6 +95,47 @@ > y[ '::-1' ] [ 4, 3, 2, 1 ] + +{{alias}}.factory( [options] ) + Returns a function for converting an array to an object supporting fancy + indexing. + + Parameters + ---------- + options: Object (optional) + Function options. + + options.strict: boolean (optional) + Boolean indicating whether to enforce strict bounds checking by default. + Default: false. + + options.cache: Object (optional) + Cache for resolving array index objects. Must have a 'get' method which + accepts a single argument: an identifier associated with an array index. + If an array index associated with a provided identifier exists, the + 'get' method should return an object having the following properties: + + - data: the underlying index array. + - type: the index type. Must be either 'mask', 'bool', or 'int'. + - dtype: the data type of the underlying array. + + If an array index is not associated with a provided identifier, the + 'get' method should return `null`. + + Returns + ------- + fcn: Function + Function for converting an array to an object supporting fancy indexing. + + Examples + -------- + > var f = {{alias}}.factory(); + > var y = f( [ 1, 2, 3, 4 ] ); + > y[ '1::2' ] + [ 2, 4 ] + > y[ '::-1' ] + [ 4, 3, 2, 1 ] + See Also -------- diff --git a/to-fancy/docs/types/index.d.ts b/to-fancy/docs/types/index.d.ts index 33348510..2cd4da18 100644 --- a/to-fancy/docs/types/index.d.ts +++ b/to-fancy/docs/types/index.d.ts @@ -20,311 +20,410 @@ /// -import { Collection, ArrayLike, AccessorArrayLike, Complex128Array, Complex64Array } from '@stdlib/types/array'; +import { Collection, ArrayLike, AccessorArrayLike, Complex128Array, Complex64Array, DataType } from '@stdlib/types/array'; /** -* Interface defining function options. +* Interface describing an index object. */ -interface Options { +interface IndexObject { /** - * Boolean indicating whether to enforce strict bounds checking (default: false). + * Underlying array index data. */ - strict?: boolean; + data: Collection | AccessorArrayLike; + + /** + * Index type. + */ + type: 'mask' | 'bool' | 'int'; + + /** + * Underlying array data type. + */ + dtype: DataType | null; } /** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Float64Array = require( '@stdlib/array/float64' ); -* -* var x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1.0, 2.0, 3.0, 4.0 ] +* Interface describing a cache for resolving array index objects. */ -declare function array2fancy( x: Float64Array, options?: Options ): Float64Array; +interface Cache { + /** + * Returns an array associated with the index object having a provided identifier. + * + * @param id - identifier + * @returns index data + */ + get( id: any ): IndexObject | null; +} /** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Float32Array = require( '@stdlib/array/float32' ); -* -* var x = new Float32Array( [ 1.0, 2.0, 3.0, 4.0 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1.0, 2.0, 3.0, 4.0 ] +* Interface describing function options. */ -declare function array2fancy( x: Float32Array, options?: Options ): Float32Array; +interface Options { + /** + * Boolean indicating whether to enforce strict bounds checking. + */ + strict?: boolean; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Complex128Array = require( '@stdlib/array/complex128' ); -* -* var x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1.0, 2.0, 3.0, 4.0 ] -*/ -declare function array2fancy( x: Complex128Array, options?: Options ): Complex128Array; + /** + * Cache for resolving array index objects. + */ + cache?: Cache; +} /** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Complex64Array = require( '@stdlib/array/complex64' ); -* -* var x = new Complex64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1.0, 2.0, 3.0, 4.0 ] +* Interface describing the main export. */ -declare function array2fancy( x: Complex64Array, options?: Options ): Complex64Array; +interface Array2Fancy { + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Float64Array = require( './../../../float64' ); + * + * var x = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1.0, 2.0, 3.0, 4.0 ] + */ + ( x: Float64Array, options?: Options ): Float64Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Int32Array = require( '@stdlib/array/int32' ); -* -* var x = new Int32Array( [ 1, 2, 3, 4 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Int32Array, options?: Options ): Int32Array; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Float32Array = require( './../../../float32' ); + * + * var x = new Float32Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1.0, 2.0, 3.0, 4.0 ] + */ + ( x: Float32Array, options?: Options ): Float32Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Int16Array = require( '@stdlib/array/int16' ); -* -* var x = new Int16Array( [ 1, 2, 3, 4 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Int16Array, options?: Options ): Int16Array; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Complex128Array = require( './../../../complex128' ); + * + * var x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1.0, 2.0, 3.0, 4.0 ] + */ + ( x: Complex128Array, options?: Options ): Complex128Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Int8Array = require( '@stdlib/array/int8' ); -* -* var x = new Int8Array( [ 1, 2, 3, 4 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Int8Array, options?: Options ): Int8Array; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Complex64Array = require( './../../../complex64' ); + * + * var x = new Complex64Array( [ 1.0, 2.0, 3.0, 4.0 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1.0, 2.0, 3.0, 4.0 ] + */ + ( x: Complex64Array, options?: Options ): Complex64Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Uint32Array = require( '@stdlib/array/uint32' ); -* -* var x = new Uint32Array( [ 1, 2, 3, 4 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Uint32Array, options?: Options ): Uint32Array; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Int32Array = require( './../../../int32' ); + * + * var x = new Int32Array( [ 1, 2, 3, 4 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Int32Array, options?: Options ): Int32Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Uint16Array = require( '@stdlib/array/uint16' ); -* -* var x = new Uint16Array( [ 1, 2, 3, 4 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Uint16Array, options?: Options ): Uint16Array; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Int16Array = require( './../../../int16' ); + * + * var x = new Int16Array( [ 1, 2, 3, 4 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Int16Array, options?: Options ): Int16Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Uint8Array = require( '@stdlib/array/uint8' ); -* -* var x = new Uint8Array( [ 1, 2, 3, 4 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Uint8Array, options?: Options ): Uint8Array; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Int8Array = require( './../../../int8' ); + * + * var x = new Int8Array( [ 1, 2, 3, 4 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Int8Array, options?: Options ): Int8Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var Uint8ClampedArray = require( '@stdlib/array/uint8c' ); -* -* var x = new Uint8ClampedArray( [ 1, 2, 3, 4 ] ); -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Uint8ClampedArray, options?: Options ): Uint8ClampedArray; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Uint32Array = require( './../../../uint32' ); + * + * var x = new Uint32Array( [ 1, 2, 3, 4 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Uint32Array, options?: Options ): Uint32Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var x = [ 1, 2, 3, 4 ]; -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Array, options?: Options ): Array; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Uint16Array = require( './../../../uint16' ); + * + * var x = new Uint16Array( [ 1, 2, 3, 4 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Uint16Array, options?: Options ): Uint16Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var toAccessorArray = require( '@stdlib/array/base/to-accessor-array' ); -* -* var x = toAccessorArray( [ 1, 2, 3, 4 ] ); -* -* var y = array2fancy( x ); -* var v = y[ ':' ]; -*/ -declare function array2fancy( x: AccessorArrayLike, options?: Options ): AccessorArrayLike; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Uint8Array = require( './../../../uint8' ); + * + * var x = new Uint8Array( [ 1, 2, 3, 4 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Uint8Array, options?: Options ): Uint8Array; -/** -* Converts an array to an object supporting fancy indexing. -* -* @param x - input array -* @param options - function options -* @param options.strict - boolean indicating whether to enforce strict bounds checking -* @returns fancy array -* -* @example -* var x = [ 1, 2, 3, 4 ]; -* -* var y = array2fancy( x ); -* // returns -* -* var v = y[ ':' ]; -* // returns [ 1, 2, 3, 4 ] -*/ -declare function array2fancy( x: Collection, options?: Options ): Collection; + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var Uint8ClampedArray = require( './../../../uint8c' ); + * + * var x = new Uint8ClampedArray( [ 1, 2, 3, 4 ] ); + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Uint8ClampedArray, options?: Options ): Uint8ClampedArray; + + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var x = [ 1, 2, 3, 4 ]; + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Array, options?: Options ): Array; + + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var toAccessorArray = require( './../../../base/to-accessor-array' ); + * + * var x = toAccessorArray( [ 1, 2, 3, 4 ] ); + * + * var y = array2fancy( x ); + * var v = y[ ':' ]; + */ + ( x: AccessorArrayLike, options?: Options ): AccessorArrayLike; + + /** + * Converts an array to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var x = [ 1, 2, 3, 4 ]; + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: Collection, options?: Options ): Collection; + + /** + * Converts an array-like value to an object supporting fancy indexing. + * + * @param x - input array + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking + * @param options.cache - cache for resolving array index objects + * @returns fancy array + * + * @example + * var x = [ 1, 2, 3, 4 ]; + * + * var y = array2fancy( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + ( x: ArrayLike, options?: Options ): ArrayLike; + + /** + * Returns a function for converting an array to an object supporting fancy indexing. + * + * @param options - function options + * @param options.strict - boolean indicating whether to enforce strict bounds checking by default + * @param options.cache - cache for resolving array index objects + * @returns function for converting an array to an object supporting fancy indexing + * + * @example + * var fcn = array2fancy.factory(); + * + * var x = [ 1, 2, 3, 4 ]; + * + * var y = fcn( x ); + * // returns + * + * var v = y[ ':' ]; + * // returns [ 1, 2, 3, 4 ] + */ + factory( options?: Options ): Array2Fancy; +} /** -* Converts an array-like value to an object supporting fancy indexing. +* Converts an array to an object supporting fancy indexing. * * @param x - input array * @param options - function options * @param options.strict - boolean indicating whether to enforce strict bounds checking +* @param options.cache - cache for resolving array index objects * @returns fancy array * * @example @@ -336,7 +435,7 @@ declare function array2fancy( x: Collection, options?: Options ) * var v = y[ ':' ]; * // returns [ 1, 2, 3, 4 ] */ -declare function array2fancy( x: ArrayLike, options?: Options ): ArrayLike; +declare var array2fancy: Array2Fancy; // EXPORTS // diff --git a/to-fancy/docs/types/test.ts b/to-fancy/docs/types/test.ts index 2996e4f5..658d18b0 100644 --- a/to-fancy/docs/types/test.ts +++ b/to-fancy/docs/types/test.ts @@ -97,8 +97,67 @@ import array2fancy = require( './index' ); array2fancy( x, { 'strict': ( x: number ): number => x } ); // $ExpectError } +// The compiler throws an error if the function is provided a `cache` option which is not valid... +{ + const x = [ 1, 2, 3, 4 ]; + + array2fancy( x, { 'cache': '5' } ); // $ExpectError + array2fancy( x, { 'cache': 5 } ); // $ExpectError + array2fancy( x, { 'cache': true } ); // $ExpectError + array2fancy( x, { 'cache': false } ); // $ExpectError + array2fancy( x, { 'cache': null } ); // $ExpectError + array2fancy( x, { 'cache': [] } ); // $ExpectError + array2fancy( x, { 'cache': {} } ); // $ExpectError + array2fancy( x, { 'cache': ( x: number ): number => x } ); // $ExpectError +} + // The compiler throws an error if the function is provided an unsupported number of arguments... { array2fancy(); // $ExpectError array2fancy( [ 1, 2, 3 ], {}, {} ); // $ExpectError } + +// Attached to the function is a `factory` method which returns a function... +{ + array2fancy.factory(); // $ExpectType Array2Fancy + array2fancy.factory( {} ); // $ExpectType Array2Fancy + array2fancy.factory( { 'strict': true } ); // $ExpectType Array2Fancy +} + +// The compiler throws an error if the `factory` method is provided a second argument which is not an object... +{ + array2fancy.factory( '5' ); // $ExpectError + array2fancy.factory( 5 ); // $ExpectError + array2fancy.factory( true ); // $ExpectError + array2fancy.factory( false ); // $ExpectError + array2fancy.factory( null ); // $ExpectError + array2fancy.factory( [] ); // $ExpectError + array2fancy.factory( ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the `factory` method is provided a `strict` option which is not a boolean... +{ + array2fancy.factory( { 'strict': '5' } ); // $ExpectError + array2fancy.factory( { 'strict': 5 } ); // $ExpectError + array2fancy.factory( { 'strict': null } ); // $ExpectError + array2fancy.factory( { 'strict': [] } ); // $ExpectError + array2fancy.factory( { 'strict': {} } ); // $ExpectError + array2fancy.factory( { 'strict': ( x: number ): number => x } ); // $ExpectError +} + +// The compiler throws an error if the `factory` method is provided a `cache` option which is not valid... +{ + array2fancy.factory( { 'cache': '5' } ); // $ExpectError + array2fancy.factory( { 'cache': 5 } ); // $ExpectError + array2fancy.factory( { 'cache': true } ); // $ExpectError + array2fancy.factory( { 'cache': false } ); // $ExpectError + array2fancy.factory( { 'cache': null } ); // $ExpectError + array2fancy.factory( { 'cache': [] } ); // $ExpectError + array2fancy.factory( { 'cache': {} } ); // $ExpectError + array2fancy.factory( { 'cache': ( x: number ): number => x } ); // $ExpectError +} + +// The compiler throws an error if the `factory` method is provided an unsupported number of arguments... +{ + array2fancy.factory( {}, {} ); // $ExpectError +} diff --git a/to-fancy/lib/defaults.js b/to-fancy/lib/defaults.js index 6776a3a8..f9ef5cf8 100644 --- a/to-fancy/lib/defaults.js +++ b/to-fancy/lib/defaults.js @@ -18,6 +18,11 @@ 'use strict'; +// MODULES // + +var ArrayIndex = require( './../../index' ); + + // MAIN // /** @@ -32,6 +37,7 @@ */ function defaults() { return { + 'cache': ArrayIndex, 'strict': false }; } diff --git a/to-fancy/lib/factory.js b/to-fancy/lib/factory.js new file mode 100644 index 00000000..21db01df --- /dev/null +++ b/to-fancy/lib/factory.js @@ -0,0 +1,169 @@ +/** +* @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 isArrayLike = require( '@stdlib/assert/is-array-like' ); +var Proxy = require( '@stdlib/proxy/ctor' ); +var arraylike2object = require( './../../base/arraylike2object' ); +var assign = require( '@stdlib/object/assign' ); +var format = require( '@stdlib/string/format' ); +var setElementWrapper = require( './set_element_wrapper.js' ); +var getSliceWrapper = require( './get_slice_wrapper.js' ); +var hasProxySupport = require( './has_proxy_support.js' ); +var defaults = require( './defaults.js' ); +var validate = require( './validate.js' ); +var validator = require( './validator.js' ); +var ctor = require( './ctor.js' ); +var get = require( './get.js' ); +var set = require( './set.js' ); + + +// MAIN // + +/** +* Returns a function for converting an array to an object supporting fancy indexing. +* +* @param {Options} options - function options +* @param {boolean} [options.strict=false] - boolean indicating whether to enforce strict bounds checking by default +* @param {Function} [options.cache] - default cache for resolving array index objects +* @throws {TypeError} options argument must be an object +* @throws {TypeError} must provide valid options +* @returns {Function} function for converting an array to an object supporting fancy indexing +* +* @example +* var array2fancy = factory(); +* +* var x = [ 1, 2, 3, 4, 5, 6 ]; +* +* var y = array2fancy( x ); +* // returns +* +* var z = y[ '1::2' ]; +* // returns [ 2, 4, 6 ] +* +* var len = z.length; +* // returns 3 +* +* var v = z[ 0 ]; +* // returns 2 +* +* v = z[ 1 ]; +* // returns 4 +* +* v = z[ 2 ]; +* // returns 6 +*/ +function factory() { + var OPTIONS; + var err; + + OPTIONS = defaults(); + if ( arguments.length ) { + err = validate( OPTIONS, arguments[ 0 ] ); + if ( err ) { + throw err; + } + } + return array2fancy; + + /** + * Converts an array to an object supporting fancy indexing. + * + * @private + * @param {ArrayLike} x - input array + * @param {Options} [options] - function options + * @param {boolean} [options.strict] - boolean indicating whether to enforce strict bounds checking + * @param {Function} [options.cache] - cache for resolving array index objects + * @throws {TypeError} first argument must be array-like + * @throws {TypeError} options argument must be an object + * @throws {TypeError} must provide valid options + * @returns {ArrayLike} fancy array + * + * @example + * var x = [ 1, 2, 3, 4, 5, 6 ]; + * + * var y = array2fancy( x ); + * // returns + * + * var z = y[ '1::2' ]; + * // returns [ 2, 4, 6 ] + * + * var len = z.length; + * // returns 3 + * + * var v = z[ 0 ]; + * // returns 2 + * + * v = z[ 1 ]; + * // returns 4 + * + * v = z[ 2 ]; + * // returns 6 + */ + function array2fancy( x ) { + var opts; + var err; + var arr; + var dt; + var o; + if ( !isArrayLike( x ) ) { + throw new TypeError( format( 'invalid argument. First argument must be array-like. Value: `%s`.', x ) ); + } + if ( hasProxySupport ) { + opts = assign( {}, OPTIONS ); + if ( arguments.length > 1 ) { + err = validate( opts, arguments[ 1 ] ); + if ( err ) { + throw err; + } + } + arr = arraylike2object( x ); + dt = arr.dtype || ''; + o = { + 'ref': x, + 'dtype': dt, + 'getter': arr.accessors[ 0 ], + 'setter': arr.accessors[ 1 ], + 'preSetElement': setElementWrapper( dt ), + 'postGetSlice': getSliceWrapper( array2fancy, opts ), + 'cache': opts.cache, + 'strict': opts.strict, + 'validator': validator( dt ), + 'array2fancy': array2fancy, + 'ctor': new Proxy( x.constructor || Array, { + 'construct': ctor( array2fancy, opts ) + }) + }; + return new Proxy( x, { + 'get': get( o ), + 'set': set( o ) + }); + } + // TODO: replace with `@stdlib/console/warn` (or equivalent once available) + console.warn( 'WARNING: Proxy objects are not supported in the current environment. Some `FancyArray` functionality may not be available.' ); // eslint-disable-line no-console + return x; + } +} + + +// EXPORTS // + +module.exports = factory; diff --git a/to-fancy/lib/index.js b/to-fancy/lib/index.js index f862cfc6..72229b29 100644 --- a/to-fancy/lib/index.js +++ b/to-fancy/lib/index.js @@ -49,7 +49,14 @@ // MODULES // +var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); var main = require( './main.js' ); +var factory = require( './factory.js' ); + + +// MAIN // + +setReadOnly( main, 'factory', factory ); // EXPORTS // diff --git a/to-fancy/lib/main.js b/to-fancy/lib/main.js index 3fe0bca8..024e7a74 100644 --- a/to-fancy/lib/main.js +++ b/to-fancy/lib/main.js @@ -20,19 +20,7 @@ // MODULES // -var isArrayLike = require( '@stdlib/assert/is-array-like' ); -var Proxy = require( '@stdlib/proxy/ctor' ); -var arraylike2object = require( './../../base/arraylike2object' ); -var format = require( '@stdlib/string/format' ); -var setElementWrapper = require( './set_element_wrapper.js' ); -var getSliceWrapper = require( './get_slice_wrapper.js' ); -var hasProxySupport = require( './has_proxy_support.js' ); -var defaults = require( './defaults.js' ); -var validate = require( './validate.js' ); -var validator = require( './validator.js' ); -var ctor = require( './ctor.js' ); -var get = require( './get.js' ); -var set = require( './set.js' ); +var factory = require( './factory.js' ); // MAIN // @@ -40,9 +28,12 @@ var set = require( './set.js' ); /** * Converts an array to an object supporting fancy indexing. * +* @name array2fancy +* @type {Function} * @param {ArrayLike} x - input array * @param {Options} [options] - function options * @param {boolean} [options.strict=false] - boolean indicating whether to enforce strict bounds checking +* @param {Function} [options.cache] - cache for resolving array index objects * @throws {TypeError} first argument must be array-like * @throws {TypeError} options argument must be an object * @throws {TypeError} must provide valid options @@ -69,48 +60,7 @@ var set = require( './set.js' ); * v = z[ 2 ]; * // returns 6 */ -function array2fancy( x ) { - var opts; - var err; - var arr; - var dt; - var o; - if ( !isArrayLike( x ) ) { - throw new TypeError( format( 'invalid argument. First argument must be array-like. Value: `%s`.', x ) ); - } - if ( hasProxySupport ) { - opts = defaults(); - if ( arguments.length > 1 ) { - err = validate( opts, arguments[ 1 ] ); - if ( err ) { - throw err; - } - } - arr = arraylike2object( x ); - dt = arr.dtype || ''; - o = { - 'ref': x, - 'dtype': dt, - 'getter': arr.accessors[ 0 ], - 'setter': arr.accessors[ 1 ], - 'preSetElement': setElementWrapper( dt ), - 'postGetSlice': getSliceWrapper( array2fancy, opts ), - 'strict': opts.strict, - 'validator': validator( dt ), - 'array2fancy': array2fancy, - 'ctor': new Proxy( x.constructor || Array, { - 'construct': ctor( array2fancy, opts ) - }) - }; - return new Proxy( x, { - 'get': get( o ), - 'set': set( o ) - }); - } - // TODO: replace with `@stdlib/console/warn` (or equivalent once available) - console.warn( 'WARNING: Proxy objects are not supported in the current environment. Some `FancyArray` functionality may not be available.' ); // eslint-disable-line no-console - return x; -} +var array2fancy = factory(); // EXPORTS // diff --git a/to-fancy/lib/validate.js b/to-fancy/lib/validate.js index 6f0ef77c..84510e40 100644 --- a/to-fancy/lib/validate.js +++ b/to-fancy/lib/validate.js @@ -23,6 +23,7 @@ var isObject = require( '@stdlib/assert/is-plain-object' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; +var isMethodIn = require( '@stdlib/assert/is-method-in' ); var format = require( '@stdlib/string/format' ); @@ -35,6 +36,7 @@ var format = require( '@stdlib/string/format' ); * @param {Object} opts - destination object * @param {Options} options - function options * @param {boolean} [options.strict] - boolean indicating whether to enforce strict bounds checking +* @param {Function} [options.cache] - cache for resolving array index objects * @returns {(Error|null)} null or an error object * * @example @@ -57,6 +59,12 @@ function validate( opts, options ) { return new TypeError( format( 'invalid option. `%s` option must be a boolean. Option: `%s`.', 'strict', opts.strict ) ); } } + if ( hasOwnProp( options, 'cache' ) ) { + opts.cache = options.cache; + if ( !isMethodIn( opts.cache, 'get' ) ) { + return new TypeError( format( 'invalid option. `%s` option is missing a `%s` method. Option: `%s`.', 'cache', 'get', opts.cache ) ); + } + } return null; } diff --git a/to-fancy/test/test.factory.js b/to-fancy/test/test.factory.js new file mode 100644 index 00000000..2410bfd8 --- /dev/null +++ b/to-fancy/test/test.factory.js @@ -0,0 +1,143 @@ +/** +* @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 proxyquire = require( 'proxyquire' ); +var Int32Array = require( './../../int32' ); +var factory = require( './../lib/factory.js' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof factory, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function throws an error if provided a first 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() { + factory( value ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid option', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + 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() { + factory({ + 'strict': value + }); + }; + } +}); + +tape( 'the function returns a function which returns an array-like object', function test( t ) { + var array2fancy; + var x; + var y; + + array2fancy = factory(); + + x = [ 1, 2, 3, 4 ]; + y = array2fancy( x ); + + t.strictEqual( y instanceof Array, true, 'returns expected value' ); + t.notEqual( y, x, 'different reference' ); + t.deepEqual( y, x, 'returns expected value' ); + + t.end(); +}); + +tape( 'if an environment does not support Proxy objects, the function returns a function which returns the input array (generic)', function test( t ) { + var array2fancy; + var factory; + var x; + var y; + + factory = proxyquire( './../lib/factory.js', { + './has_proxy_support.js': false + }); + array2fancy = factory(); + + x = [ 1, 2, 3, 4 ]; + y = array2fancy( x ); + + t.strictEqual( y, x, 'returns expected value' ); + t.end(); +}); + +tape( 'if an environment does not support Proxy objects, the function returns a function which returns the input array (typed)', function test( t ) { + var array2fancy; + var factory; + var x; + var y; + + factory = proxyquire( './../lib/factory.js', { + './has_proxy_support.js': false + }); + array2fancy = factory(); + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + y = array2fancy( x ); + + t.strictEqual( y, x, 'returns expected value' ); + t.end(); +}); diff --git a/to-fancy/test/test.js b/to-fancy/test/test.js index 71ea5060..be5024e7 100644 --- a/to-fancy/test/test.js +++ b/to-fancy/test/test.js @@ -21,20 +21,10 @@ // MODULES // var tape = require( 'tape' ); -var proxyquire = require( 'proxyquire' ); -var hasProxySupport = require( '@stdlib/assert/has-proxy-support' ); -var Int32Array = require( './../../int32' ); -var propertiesIn = require( '@stdlib/utils/properties-in' ); +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); var array2fancy = require( './../lib' ); -// VARIABLES // - -var opts = { - 'skip': !hasProxySupport() -}; - - // TESTS // tape( 'main export is a function', function test( t ) { @@ -43,396 +33,8 @@ tape( 'main export is a function', function test( t ) { t.end(); }); -tape( 'the function throws an error if provided a first argument which is not an array-like value', function test( t ) { - var values; - var i; - - values = [ - 5, - NaN, - true, - false, - null, - void 0, - {} - ]; - 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() { - array2fancy( value ); - }; - } -}); - -tape( 'the function throws an error if provided a first argument which is not an array-like value (options)', function test( t ) { - var values; - var i; - - values = [ - 5, - NaN, - true, - false, - null, - void 0, - {} - ]; - 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() { - array2fancy( value, {} ); - }; - } -}); - -tape( 'the function throws an error if provided a second 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() { - array2fancy( [ 1, 2, 3 ], value ); - }; - } -}); - -tape( 'the function throws an error if provided an invalid option', function test( t ) { - var values; - var i; - - values = [ - '5', - 5, - NaN, - 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() { - array2fancy( [ 1, 2, 3 ], { - 'strict': value - }); - }; - } -}); - -tape( 'if an environment does not support Proxy objects, the function returns the input array (generic)', function test( t ) { - var array2fancy; - var x; - var y; - - array2fancy = proxyquire( './../lib/main.js', { - './has_proxy_support.js': false - }); - - x = [ 1, 2, 3, 4 ]; - y = array2fancy( x ); - - t.strictEqual( y, x, 'returns expected value' ); - t.end(); -}); - -tape( 'if an environment does not support Proxy objects, the function returns the input array (typed)', function test( t ) { - var array2fancy; - var x; - var y; - - array2fancy = proxyquire( './../lib/main.js', { - './has_proxy_support.js': false - }); - - x = new Int32Array( [ 1, 2, 3, 4 ] ); - y = array2fancy( x ); - - t.strictEqual( y, x, 'returns expected value' ); - t.end(); -}); - -tape( 'the function returns an array-like object which satisfies the same instance check (generic)', function test( t ) { - var x; - var y; - - x = [ 1, 2, 3, 4 ]; - y = array2fancy( x ); - - t.notEqual( y, x, 'different reference' ); - t.strictEqual( y instanceof Array, true, 'returns expected value' ); - t.end(); -}); - -tape( 'the function returns an array-like object which satisfies the same instance check (typed)', function test( t ) { - var x; - var y; - - x = new Int32Array( [ 1, 2, 3, 4 ] ); - y = array2fancy( x ); - - t.notEqual( y, x, 'different reference' ); - t.strictEqual( y instanceof Int32Array, true, 'returns expected value' ); - t.end(); -}); - -tape( 'the function returns an array-like object having the same properties, both own and inherited, as the input array (generic)', function test( t ) { - var x; - var y; - - x = [ 1, 2, 3, 4 ]; - y = array2fancy( x ); - - t.deepEqual( propertiesIn( y ), propertiesIn( x ), 'returns expected value' ); - t.end(); -}); - -tape( 'the function returns an array-like object having the same properties, both own and inherited, as the input array (typed)', function test( t ) { - var x; - var y; - - x = new Int32Array( [ 1, 2, 3, 4 ] ); - y = array2fancy( x ); - - t.deepEqual( propertiesIn( y ), propertiesIn( x ), 'returns expected value' ); - t.end(); -}); - -tape( 'the function returns an array-like object supporting bracket syntax for element retrieval (generic)', function test( t ) { - var x; - var y; - var i; - - x = [ 1, 2, 3, 4 ]; - y = array2fancy( x ); - - for ( i = 0; i < x.length; i++ ) { - t.strictEqual( y[ i ], x[ i ], 'returns expected value' ); - } - t.end(); -}); - -tape( 'the function returns an array-like object supporting bracket syntax for element retrieval (typed)', function test( t ) { - var x; - var y; - var i; - - x = new Int32Array( [ 1, 2, 3, 4 ] ); - y = array2fancy( x ); - - for ( i = 0; i < x.length; i++ ) { - t.strictEqual( y[ i ], x[ i ], 'returns expected value' ); - } - t.end(); -}); - -tape( 'the function returns an array-like object supporting the calling of input array methods (generic)', function test( t ) { - var expected; - var actual; - var x; - var y; - - x = [ 1, 2, 3, 4 ]; - y = array2fancy( x ); - - expected = [ 2, 4, 6, 8 ]; - actual = y.map( fcn ); - - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - t.end(); - - function fcn( v ) { - return v * 2; - } -}); - -tape( 'the function returns an array-like object supporting the calling of input array methods (typed)', function test( t ) { - var expected; - var actual; - var x; - var y; - - x = new Int32Array( [ 1, 2, 3, 4 ] ); - y = array2fancy( x ); - - expected = new Int32Array( [ 2, 4, 6, 8 ] ); - actual = y.map( fcn ); - - t.strictEqual( actual instanceof Int32Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - t.end(); - - function fcn( v ) { - return v * 2; - } -}); - -tape( 'the function returns an array-like object supporting the return of array instances supporting slice expressions (generic)', opts, function test( t ) { - var expected; - var actual; - var x; - var y; - var z; - - x = [ 1, 2, 3, 4 ]; - y = array2fancy( x ); - - expected = [ 1, 2, 3, 4 ]; - actual = y.slice(); - - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - t.strictEqual( x[ ':' ], void 0, 'returns expected value' ); - - expected = [ 1, 2, 3, 4 ]; - actual = y[ ':' ]; - - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - z = actual; - - expected = [ 1, 2, 3, 4 ]; - actual = z[ ':' ]; - - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - t.end(); -}); - -tape( 'the function returns an array-like object supporting the return of array instances supporting slice expressions (typed)', opts, function test( t ) { - var expected; - var actual; - var x; - var y; - var z; - - x = new Int32Array( [ 1, 2, 3, 4 ] ); - y = array2fancy( x ); - - expected = new Int32Array( [ 1, 2, 3, 4 ] ); - actual = y.slice(); - - t.strictEqual( actual instanceof Int32Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - t.strictEqual( x[ ':' ], void 0, 'returns expected value' ); - - expected = new Int32Array( [ 1, 2, 3, 4 ] ); - actual = y[ ':' ]; - - t.strictEqual( actual instanceof Int32Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - z = actual; - - expected = new Int32Array( [ 1, 2, 3, 4 ] ); - actual = z[ ':' ]; - - t.strictEqual( actual instanceof Int32Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - t.end(); -}); - -tape( 'the function returns an array-like object supporting constructor access (generic)', function test( t ) { - var expected; - var actual; - var x; - var y; - - x = [ 1, 2, 3, 4 ]; - y = array2fancy( x ); - - expected = []; - actual = new y.constructor(); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - actual = new y.constructor( 1 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.strictEqual( actual.length, 1, 'returns expected value' ); - - expected = [ 1, 2 ]; - actual = new y.constructor( 1, 2 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3 ]; - actual = new y.constructor( 1, 2, 3 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3, 4 ]; - actual = new y.constructor( 1, 2, 3, 4 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3, 4, 5 ]; - actual = new y.constructor( 1, 2, 3, 4, 5 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3, 4, 5, 6 ]; - actual = new y.constructor( 1, 2, 3, 4, 5, 6 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3, 4, 5, 6, 7 ]; - actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; - actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7, 8 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; - actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7, 8, 9 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - - expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; - actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ); - t.strictEqual( actual instanceof Array, true, 'returns expected value' ); - t.deepEqual( actual, expected, 'returns expected value' ); - +tape( 'attached to the main export is a `factory` method', function test( t ) { + t.strictEqual( hasOwnProp( array2fancy, 'factory' ), true, 'has property' ); + t.strictEqual( typeof array2fancy.factory, 'function', 'has method' ); t.end(); }); diff --git a/to-fancy/test/test.main.js b/to-fancy/test/test.main.js new file mode 100644 index 00000000..4f332687 --- /dev/null +++ b/to-fancy/test/test.main.js @@ -0,0 +1,405 @@ +/** +* @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 hasProxySupport = require( '@stdlib/assert/has-proxy-support' ); +var Int32Array = require( './../../int32' ); +var propertiesIn = require( '@stdlib/utils/properties-in' ); +var array2fancy = require( './../lib' ); + + +// VARIABLES // + +var opts = { + 'skip': !hasProxySupport() +}; + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof array2fancy, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function throws an error if provided a first argument which is not an array-like value', function test( t ) { + var values; + var i; + + values = [ + 5, + NaN, + true, + false, + null, + void 0, + {} + ]; + 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() { + array2fancy( value ); + }; + } +}); + +tape( 'the function throws an error if provided a first argument which is not an array-like value (options)', function test( t ) { + var values; + var i; + + values = [ + 5, + NaN, + true, + false, + null, + void 0, + {} + ]; + 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() { + array2fancy( value, {} ); + }; + } +}); + +tape( 'the function throws an error if provided a second 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() { + array2fancy( [ 1, 2, 3 ], value ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid option', function test( t ) { + var values; + var i; + + values = [ + '5', + 5, + NaN, + 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() { + array2fancy( [ 1, 2, 3 ], { + 'strict': value + }); + }; + } +}); + +tape( 'the function returns an array-like object which satisfies the same instance check (generic)', function test( t ) { + var x; + var y; + + x = [ 1, 2, 3, 4 ]; + y = array2fancy( x ); + + t.notEqual( y, x, 'different reference' ); + t.strictEqual( y instanceof Array, true, 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns an array-like object which satisfies the same instance check (typed)', function test( t ) { + var x; + var y; + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + y = array2fancy( x ); + + t.notEqual( y, x, 'different reference' ); + t.strictEqual( y instanceof Int32Array, true, 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns an array-like object having the same properties, both own and inherited, as the input array (generic)', function test( t ) { + var x; + var y; + + x = [ 1, 2, 3, 4 ]; + y = array2fancy( x ); + + t.deepEqual( propertiesIn( y ), propertiesIn( x ), 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns an array-like object having the same properties, both own and inherited, as the input array (typed)', function test( t ) { + var x; + var y; + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + y = array2fancy( x ); + + t.deepEqual( propertiesIn( y ), propertiesIn( x ), 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns an array-like object supporting bracket syntax for element retrieval (generic)', function test( t ) { + var x; + var y; + var i; + + x = [ 1, 2, 3, 4 ]; + y = array2fancy( x ); + + for ( i = 0; i < x.length; i++ ) { + t.strictEqual( y[ i ], x[ i ], 'returns expected value' ); + } + t.end(); +}); + +tape( 'the function returns an array-like object supporting bracket syntax for element retrieval (typed)', function test( t ) { + var x; + var y; + var i; + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + y = array2fancy( x ); + + for ( i = 0; i < x.length; i++ ) { + t.strictEqual( y[ i ], x[ i ], 'returns expected value' ); + } + t.end(); +}); + +tape( 'the function returns an array-like object supporting the calling of input array methods (generic)', function test( t ) { + var expected; + var actual; + var x; + var y; + + x = [ 1, 2, 3, 4 ]; + y = array2fancy( x ); + + expected = [ 2, 4, 6, 8 ]; + actual = y.map( fcn ); + + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + t.end(); + + function fcn( v ) { + return v * 2; + } +}); + +tape( 'the function returns an array-like object supporting the calling of input array methods (typed)', function test( t ) { + var expected; + var actual; + var x; + var y; + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + y = array2fancy( x ); + + expected = new Int32Array( [ 2, 4, 6, 8 ] ); + actual = y.map( fcn ); + + t.strictEqual( actual instanceof Int32Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + t.end(); + + function fcn( v ) { + return v * 2; + } +}); + +tape( 'the function returns an array-like object supporting the return of array instances supporting slice expressions (generic)', opts, function test( t ) { + var expected; + var actual; + var x; + var y; + var z; + + x = [ 1, 2, 3, 4 ]; + y = array2fancy( x ); + + expected = [ 1, 2, 3, 4 ]; + actual = y.slice(); + + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.strictEqual( x[ ':' ], void 0, 'returns expected value' ); + + expected = [ 1, 2, 3, 4 ]; + actual = y[ ':' ]; + + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + z = actual; + + expected = [ 1, 2, 3, 4 ]; + actual = z[ ':' ]; + + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function returns an array-like object supporting the return of array instances supporting slice expressions (typed)', opts, function test( t ) { + var expected; + var actual; + var x; + var y; + var z; + + x = new Int32Array( [ 1, 2, 3, 4 ] ); + y = array2fancy( x ); + + expected = new Int32Array( [ 1, 2, 3, 4 ] ); + actual = y.slice(); + + t.strictEqual( actual instanceof Int32Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.strictEqual( x[ ':' ], void 0, 'returns expected value' ); + + expected = new Int32Array( [ 1, 2, 3, 4 ] ); + actual = y[ ':' ]; + + t.strictEqual( actual instanceof Int32Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + z = actual; + + expected = new Int32Array( [ 1, 2, 3, 4 ] ); + actual = z[ ':' ]; + + t.strictEqual( actual instanceof Int32Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'the function returns an array-like object supporting constructor access (generic)', function test( t ) { + var expected; + var actual; + var x; + var y; + + x = [ 1, 2, 3, 4 ]; + y = array2fancy( x ); + + expected = []; + actual = new y.constructor(); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + actual = new y.constructor( 1 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.strictEqual( actual.length, 1, 'returns expected value' ); + + expected = [ 1, 2 ]; + actual = new y.constructor( 1, 2 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3 ]; + actual = new y.constructor( 1, 2, 3 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3, 4 ]; + actual = new y.constructor( 1, 2, 3, 4 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3, 4, 5 ]; + actual = new y.constructor( 1, 2, 3, 4, 5 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3, 4, 5, 6 ]; + actual = new y.constructor( 1, 2, 3, 4, 5, 6 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3, 4, 5, 6, 7 ]; + actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; + actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7, 8 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; + actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7, 8, 9 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; + actual = new y.constructor( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ); + t.strictEqual( actual instanceof Array, true, 'returns expected value' ); + t.deepEqual( actual, expected, 'returns expected value' ); + + t.end(); +}); diff --git a/to-fancy/test/test.validate.js b/to-fancy/test/test.validate.js index c8350a1e..68910ccd 100644 --- a/to-fancy/test/test.validate.js +++ b/to-fancy/test/test.validate.js @@ -81,6 +81,33 @@ tape( 'the function returns an error if provided a `strict` option which is not t.end(); }); +tape( 'the function returns an error if provided a `cache` option which is not valid', 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( {}, { + 'cache': 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; @@ -88,12 +115,16 @@ tape( 'the function returns `null` if all options are valid', function test( t ) var err; options = { - 'strict': true + 'strict': true, + 'cache': { + 'get': get + } }; opts = {}; expected = { - 'strict': true + 'strict': true, + 'cache': options.cache }; err = validate( opts, options ); @@ -102,6 +133,10 @@ tape( 'the function returns `null` if all options are valid', function test( t ) t.deepEqual( opts, expected, 'returns expected value' ); t.end(); + + function get() { + // No-op... + } }); tape( 'the function ignores unrecognized options', function test( t ) {