Skip to content

Commit

Permalink
feat: add support for using index arrays in indexing expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
kgryte committed Feb 13, 2024
1 parent 9d78727 commit 993f468
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 11 deletions.
26 changes: 26 additions & 0 deletions lib/node_modules/@stdlib/array/to-fancy/examples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@

'use strict';

var Uint8Array = require( '@stdlib/array/uint8' );
var Int32Array = require( '@stdlib/array/int32' );
var idx = require( '@stdlib/array/index' );
var array2fancy = require( './../lib' );

var x = [ 1, 2, 3, 4, 5, 6 ];
var y = array2fancy( x );
// returns <Array>

// Slice retrieval:
var z = y[ '1::2' ];
console.log( z );
// => [ 2, 4, 6 ]
Expand All @@ -36,6 +40,7 @@ z = y[ '1:4' ];
console.log( z );
// => [ 2, 3, 4 ]

// Slice assignment:
y[ '4:1:-1' ] = 10;
z = y[ ':' ];
console.log( z );
Expand All @@ -45,3 +50,24 @@ y[ '2:5' ] = [ -10, -9, -8 ];
z = y[ ':' ];
console.log( z );
// => [ 1, 2, -10, -9, -8, 6 ]

// Array index retrieval:
var i = idx( [ 1, 3, 4 ] ); // integer index array
z = y[ i ];
console.log( z );
// => [ 2, -9, -8 ]

i = idx( [ true, false, false, true, true, true ] ); // boolean array
z = y[ i ];
console.log( z );
// => [ 1, -9, -8, 6 ]

i = idx( new Uint8Array( [ 0, 0, 1, 0, 0, 1 ] ) ); // mask array
z = y[ i ];
console.log( z );
// => [ 1, 2, -9, -8 ]

i = idx( new Int32Array( [ 0, 0, 1, 1, 2, 2 ] ) ); // integer index array
z = y[ i ];
console.log( z );
// => [ 1, 1, 2, 2, -10, -10 ]
4 changes: 2 additions & 2 deletions lib/node_modules/@stdlib/array/to-fancy/lib/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var arraylike2object = require( '@stdlib/array/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 getArrayWrapper = require( './get_array_wrapper.js' );
var hasProxySupport = require( './has_proxy_support.js' );
var defaults = require( './defaults.js' );
var validate = require( './validate.js' );
Expand Down Expand Up @@ -143,7 +143,7 @@ function factory() {
'getter': arr.accessors[ 0 ],
'setter': arr.accessors[ 1 ],
'preSetElement': setElementWrapper( dt ),
'postGetSlice': getSliceWrapper( array2fancy, opts ),
'postGetArray': getArrayWrapper( array2fancy, opts ),
'cache': opts.cache,
'strict': opts.strict,
'validator': validator( dt ),
Expand Down
11 changes: 9 additions & 2 deletions lib/node_modules/@stdlib/array/to-fancy/lib/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var hasProperty = require( '@stdlib/assert/has-property' );
var isIntegerString = require( './is_integer_string.js' );
var isArrayIndexString = require( './is_array_index_string.js' );
var getElements = require( './get_elements.js' );
var getElement = require( './get_element.js' );
var getValue = require( './get_value.js' );
var getSlice = require( './get_slice.js' );
Expand All @@ -38,7 +40,8 @@ var getSlice = require( './get_slice.js' );
* @param {Function} ctx.getter - accessor for retrieving array elements
* @param {boolean} ctx.strict - boolean indicating whether to enforce strict bounds checking
* @param {Function} ctx.ctor - proxied array constructor
* @param {Function} ctx.postGetSlice - function to process a retrieved slice
* @param {Function} ctx.postGetArray - function to process a retrieved array
* @param {Object} ctx.cache - cache for resolving array index objects
* @returns {Function} handler
*/
function factory( ctx ) {
Expand All @@ -53,6 +56,7 @@ function factory( ctx ) {
* @param {Object} receiver - the proxy object or an object inheriting from the proxy
* @throws {Error} invalid slice operation
* @throws {RangeError} slice exceeds array bounds
* @throws {RangeError} index exceeds array bounds
* @returns {*} result
*/
function get( target, property, receiver ) {
Expand All @@ -62,7 +66,10 @@ function factory( ctx ) {
if ( hasProperty( target, property ) || !isString( property ) ) {
return getValue( target, property, receiver, ctx );
}
return getSlice( target, property, receiver, ctx );
if ( isArrayIndexString( property ) ) {
return getElements( target, property, ctx );
}
return getSlice( target, property, ctx );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
// MAIN //

/**
* Returns a wrapper function for processing slices after retrieval.
* Returns a wrapper function for processing arrays after retrieval.
*
* @private
* @param {Function} array2fancy - function for creating a proxied array
* @param {Object} opts - options
* @param {boolean} opts.strict - boolean indicating whether to perform strict bounds checking
* @param {Function} opts.cache - cache for resolving array index objects
* @returns {Function} wrapper function
*/
function wrapper( array2fancy, opts ) {
Expand Down
63 changes: 63 additions & 0 deletions lib/node_modules/@stdlib/array/to-fancy/lib/get_elements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @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 take = require( '@stdlib/array/take' );
var mskfilter = require( '@stdlib/array/base/mskfilter' );
var mskreject = require( '@stdlib/array/base/mskreject' );
var format = require( '@stdlib/string/format' );
var prop2array = require( './prop2array.js' );


// MAIN //

/**
* Returns the elements specified by an array index.
*
* @private
* @param {Object} target - target object
* @param {string} property - index string
* @param {Object} ctx - context object
* @param {boolean} ctx.strict - boolean indicating whether to enforce strict bounds checking
* @param {Object} ctx.cache - cache for resolving array index objects
* @param {Function} ctx.postGetArray - function to process a retrieved array
* @throws {Error} invalid array index
* @throws {RangeError} index exceeds array bounds
* @returns {Collection} result
*/
function getElements( target, property, ctx ) {
var idx = prop2array( property, ctx.cache );
if ( idx.type === 'int' ) {
return ctx.postGetArray( take( target, idx.data ) );
}
if ( idx.type === 'bool' ) {
return ctx.postGetArray( mskfilter( target, idx.data ) );
}
if ( idx.type === 'mask' ) {
return ctx.postGetArray( mskreject( target, idx.data ) );
}
throw new Error( format( 'invalid operation. Unrecognized array index type. Value: `%s`.', idx.type ) );
}


// EXPORTS //

module.exports = getElements;
7 changes: 3 additions & 4 deletions lib/node_modules/@stdlib/array/to-fancy/lib/get_slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,21 @@ var prop2slice = require( './prop2slice.js' );
* @private
* @param {Object} target - target object
* @param {string} property - property name
* @param {Object} receiver - the proxy object or an object inheriting from the proxy
* @param {Object} ctx - context object
* @param {Function} ctx.postGetSlice - function to process a retrieved slice
* @param {Function} ctx.postGetArray - function to process a retrieved array
* @param {boolean} ctx.strict - boolean indicating whether to enforce strict bounds checking
* @throws {Error} invalid slice operation
* @throws {RangeError} slice exceeds array bounds
* @returns {(Collection|void)} result
*/
function getSlice( target, property, receiver, ctx ) {
function getSlice( target, property, ctx ) {
var s = prop2slice( target, property, ctx.strict );
if ( s === null ) {
// Ensure consistency with normal array behavior by returning `undefined` for any "unrecognized" property name:
return;
}
try {
return ctx.postGetSlice( slice( target, s, ctx.strict ) );
return ctx.postGetArray( slice( target, s, ctx.strict ) );
} catch ( err ) {
// In principle, we should only error when in "strict" mode and a slice exceeds array bounds...
throw new err.constructor( errMessage( err.message ) );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @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 isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var RE_ARRAY_INDEX = require( './re_array_index.js' );


// MAIN //

/**
* Tests if an indexing expression is a serialized array index.
*
* @private
* @param {(string|symbol)} prop - property name
* @returns {boolean} result
*
* @example
* var out = isArrayIndexString( 'ArrayIndex<0>' );
* // returns true
*
* @example
* var out = isArrayIndexString( ':' );
* // returns false
*/
function isArrayIndexString( prop ) {
return ( isString( prop ) && RE_ARRAY_INDEX.test( prop ) );
}


// EXPORTS //

module.exports = isArrayIndexString;
69 changes: 69 additions & 0 deletions lib/node_modules/@stdlib/array/to-fancy/lib/prop2array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @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 trim = require( '@stdlib/string/base/trim' );
var format = require( '@stdlib/string/format' );


// FUNCTIONS //

/**
* Extracts an array index identifier from an array index indexing expression.
*
* @private
* @param {string} str - input string
* @returns {string} identifier
*
* @example
* var str = 'ArrayIndex<0>';
*
* var id = getIdentifier( str );
* // returns '0'
*/
function getIdentifier( str ) {
return str.substring( 11, str.length-1 ); // ArrayIndex<XX> => XX
}


// MAIN //

/**
* Converts an indexing expression to an array index.
*
* @private
* @param {string} property - property name
* @param {Object} cache - cache for resolving array index objects
* @throws {Error} invalid array index
* @returns {(Object|null)} index object (or null)
*/
function prop2array( property, cache ) {
var o = cache.get( getIdentifier( trim( property ) ) );
if ( o === null ) {
throw new Error( format( 'invalid operation. Unable to resolve array index. Value: `%s`.', property ) );
}
return o;
}


// EXPORTS //

module.exports = prop2array;
47 changes: 47 additions & 0 deletions lib/node_modules/@stdlib/array/to-fancy/lib/re_array_index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @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 //

/**
* Regular expression for testing whether a string is a serialized array index.
*
* @private
* @name RE_ARRAY_INDEX
* @type {RegExp}
*
* @example
* var bool = RE_ARRAY_INDEX.test( 'ArrayIndex<0>' );
* // returns true
*
* @example
* var bool = RE_ARRAY_INDEX.test( '0' );
* // returns false
*
* @example
* var bool = RE_ARRAY_INDEX.test( 'Slice(0,10,2)' );
* // returns false
*/
var RE_ARRAY_INDEX = /\s*ArrayIndex<[^>]+>\s*/;


// EXPORTS //

module.exports = RE_ARRAY_INDEX;
4 changes: 2 additions & 2 deletions lib/node_modules/@stdlib/array/to-fancy/lib/resolve_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var format = require( '@stdlib/string/format' );
* @param {string} str - integer string
* @param {NonNegativeInteger} max - index upper bound (exclusive)
* @param {boolean} strict - boolean indicating whether to enforce strict bounds checking
* @throws {RangeError} slice exceeds array bounds
* @throws {RangeError} index exceeds array bounds
* @returns {integer} integer index
*
* @example
Expand All @@ -52,7 +52,7 @@ function resolveIndex( str, max, strict ) {
i = normalizeIndex( idx, max-1 );
if ( i === -1 ) {
if ( strict ) {
throw new RangeError( format( 'invalid operation. Slice exceeds array bounds.' ) );
throw new RangeError( format( 'invalid operation. Index exceeds array bounds.' ) );
}
// Return the non-normalized index, as this should fallback to default property handling and returning "undefined":
return idx;
Expand Down
Loading

1 comment on commit 993f468

@stdlib-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage Report

Package Statements Branches Functions Lines
array/index $\color{red}609/729$
$\color{green}+83.54\%$
$\color{red}21/26$
$\color{green}+80.77\%$
$\color{red}3/14$
$\color{green}+21.43\%$
$\color{red}609/729$
$\color{green}+83.54\%$
array/to-fancy $\color{red}2034/2059$
$\color{green}+98.79\%$
$\color{red}199/202$
$\color{green}+98.51\%$
$\color{red}36/39$
$\color{green}+92.31\%$
$\color{red}2034/2059$
$\color{green}+98.79\%$

The above coverage report was generated for the changes in this push.

Please sign in to comment.