Skip to content

Commit 7d12ad0

Browse files
committed
feat: add array/to-fancy
1 parent 7e48faf commit 7d12ad0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+9116
-0
lines changed
Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
<!--
2+
3+
@license Apache-2.0
4+
5+
Copyright (c) 2024 The Stdlib Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
-->
20+
21+
# array2fancy
22+
23+
> Convert an array to an object supporting fancy indexing.
24+
25+
<!-- Section to include introductory text. Make sure to keep an empty line after the intro `section` element and another before the `/section` close. -->
26+
27+
<section class="intro">
28+
29+
An array supporting **fancy indexing** is an array which supports slicing via indexing expressions for both retrieval and assignment.
30+
31+
```javascript
32+
var array2fancy = require( '@stdlib/array/to-fancy' );
33+
34+
// Create a plain array:
35+
var x = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
36+
37+
// Turn the plain array into a "fancy" array:
38+
var y = array2fancy( x );
39+
40+
// Select the first 3 elements:
41+
var v = y[ ':3' ];
42+
// returns [ 1, 2, 3 ]
43+
44+
// Select every other element, starting from the second element:
45+
v = y[ '1::2' ];
46+
// returns [ 2, 4, 6, 8 ]
47+
48+
// Select every other element, in reverse order, starting with the least element:
49+
v = y[ '::-2' ];
50+
// returns [ 8, 6, 4, 2 ]
51+
52+
// Set all elements to the same value:
53+
y[ ':' ] = 9;
54+
55+
// Create a shallow copy by selecting all elements:
56+
v = y[ ':' ];
57+
// returns [ 9, 9, 9, 9, 9, 9, 9, 9 ]
58+
```
59+
60+
</section>
61+
62+
<!-- /.intro -->
63+
64+
<!-- Package usage documentation. -->
65+
66+
<section class="usage">
67+
68+
## Usage
69+
70+
```javascript
71+
var array2fancy = require( '@stdlib/array/to-fancy' );
72+
```
73+
74+
#### array2fancy( x\[, options] )
75+
76+
Converts an array to an object supporting fancy indexing.
77+
78+
```javascript
79+
var Slice = require( '@stdlib/slice/ctor' );
80+
81+
var x = [ 1, 2, 3, 4 ];
82+
83+
var y = array2fancy( x );
84+
// returns <Array>
85+
86+
// Normal element access:
87+
var v = y[ 0 ];
88+
// returns 1
89+
90+
v = y[ 1 ];
91+
// returns 2
92+
93+
// Using negative integers:
94+
v = y[ -1 ];
95+
// returns 4
96+
97+
v = y[ -2 ];
98+
// returns 3
99+
100+
// Using subsequence expressions:
101+
v = y[ '1::2' ];
102+
// returns [ 2, 4 ]
103+
104+
// Using Slice objects:
105+
v = y[ new Slice( 1, null, 2 ) ];
106+
// returns [ 2, 4 ]
107+
108+
// Assignment:
109+
y[ '1:3' ] = 5;
110+
v = y[ ':' ];
111+
// returns [ 1, 5, 5, 4 ]
112+
```
113+
114+
The function supports the following options:
115+
116+
- **strict**: boolean indicating whether to enforce strict bounds checking. Default: `false`.
117+
118+
By default, the function returns a fancy array which does **not** enforce strict bounds checking. For example,
119+
120+
```javascript
121+
var y = array2fancy( [ 1, 2, 3, 4 ] );
122+
123+
var v = y[ 10 ];
124+
// returns undefined
125+
```
126+
127+
To enforce strict bounds checking, set the `strict` option to `true`.
128+
129+
<!-- run throws: true -->
130+
131+
```javascript
132+
var y = array2fancy( [ 1, 2, 3, 4 ], {
133+
'strict': true
134+
});
135+
136+
var v = y[ 10 ];
137+
// throws <RangeError>
138+
```
139+
140+
</section>
141+
142+
<!-- /.usage -->
143+
144+
<!-- Package usage notes. Make sure to keep an empty line after the `section` element and another before the `/section` close. -->
145+
146+
<section class="notes">
147+
148+
* * *
149+
150+
## Notes
151+
152+
- 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.
153+
- A fancy array supports indexing using positive and negative integers (both numeric literals and strings), [`Slice`][@stdlib/slice/ctor] instances, and [subsequence expressions][@stdlib/slice/seq2slice].
154+
- A fancy array supports all properties and methods of the input array, and, thus, a fancy array can be consumed by any API which supports array-like objects.
155+
- Indexing expressions provide a convenient and powerful means for creating and operating on array views; however, their use does entail a performance cost. Indexing expressions are best suited for interactive use (e.g., in the [REPL][@stdlib/repl]) and scripting. For performance critical applications, prefer equivalent functional APIs supporting array-like objects.
156+
- In older JavaScript environments which do **not** support [`Proxy`][@stdlib/proxy/ctor] objects, the use of indexing expressions is **not** supported.
157+
158+
### Broadcasting
159+
160+
Fancy arrays support **broadcasting** in which assigned scalars and single-element arrays are repeated (without additional memory allocation) to match the length of a target array instance.
161+
162+
```javascript
163+
var y = array2fancy( [ 1, 2, 3, 4 ] );
164+
165+
// Broadcast a scalar:
166+
y[ ':' ] = 5;
167+
var v = y[ ':' ];
168+
// returns [ 5, 5, 5, 5 ]
169+
170+
// Broadcast a single-element array:
171+
y[ ':' ] = [ 6 ];
172+
v = y[ ':' ];
173+
// returns [ 6, 6, 6, 6 ]
174+
```
175+
176+
Fancy array broadcasting follows the [same rules][@stdlib/ndarray/base/broadcast-shapes] as for [ndarrays][@stdlib/ndarray/ctor]. Consequently, when assigning arrays to slices, the array on the right-hand-side must be broadcast-compatible with number of elements in the slice. For example,
177+
178+
```javascript
179+
var y = array2fancy( [ 1, 2, 3, 4 ] );
180+
181+
y[ ':' ] = [ 5, 6, 7, 8 ];
182+
var v = y[ ':' ];
183+
// returns [ 5, 6, 7, 8 ]
184+
185+
y[ '1::2' ] = [ 9, 10 ];
186+
v = y[ ':' ];
187+
// returns [ 5, 9, 7, 10 ]
188+
189+
y[ '1::2' ] = [ 11 ];
190+
v = y[ ':' ];
191+
// returns [ 5, 11, 7, 11 ]
192+
193+
y[ '1::2' ] = 12;
194+
v = y[ ':' ];
195+
// returns [ 5, 12, 7, 12 ]
196+
197+
// Out-of-bounds slices (i.e., slices with zero elements):
198+
y[ '10:20' ] = [ 13 ];
199+
v = y[ ':' ];
200+
// returns [ 5, 12, 7, 12 ]
201+
202+
y[ '10:20' ] = 13;
203+
v = y[ ':' ];
204+
// returns [ 5, 12, 7, 12 ]
205+
206+
y[ '10:20' ] = [];
207+
v = y[ ':' ];
208+
// returns [ 5, 12, 7, 12 ]
209+
```
210+
211+
are all valid. However,
212+
213+
<!-- run throws: true -->
214+
215+
```javascript
216+
var y = array2fancy( [ 1, 2, 3, 4 ] );
217+
218+
y[ ':' ] = [ 5, 6 ];
219+
// throws <Error>
220+
221+
// Out-of-bounds slice (i.e., a slice with zero elements):
222+
y[ '10:20' ] = [ 8, 9, 10, 11 ];
223+
// throws <Error>
224+
```
225+
226+
### Casting
227+
228+
Fancy arrays support [(mostly) safe casts][@stdlib/array/mostly-safe-casts] (i.e., any cast which can be performed without overflow or loss of precision, with the exception of floating-point arrays which are also allowed to downcast from higher precision to lower precision).
229+
230+
```javascript
231+
var Uint8Array = require( '@stdlib/array/uint8' );
232+
var Int32Array = require( '@stdlib/array/int32' );
233+
234+
var x = new Int32Array( [ 1, 2, 3, 4 ] );
235+
var y = array2fancy( x );
236+
// returns <Int32Array>
237+
238+
// 8-bit unsigned integer values can be safely cast to 32-bit signed integer values:
239+
y[ ':' ] = new Uint8Array( [ 5, 6, 7, 8 ] );
240+
var v = y[ ':' ];
241+
// returns <Int32Array>[ 5, 6, 7, 8 ]
242+
```
243+
244+
When attempting to perform an unsafe cast, fancy arrays will raise an exception.
245+
246+
<!-- run throws: true -->
247+
248+
```javascript
249+
var Uint8Array = require( '@stdlib/array/uint8' );
250+
251+
var x = new Uint8Array( [ 1, 2, 3, 4 ] );
252+
var y = array2fancy( x );
253+
// returns <Uint8Array>
254+
255+
// Attempt to assign a non-integer value:
256+
y[ ':' ] = 3.14;
257+
// throws <TypeError>
258+
259+
// Attempt to assign a negative value:
260+
y[ ':' ] = -3;
261+
// throws <TypeError>
262+
```
263+
264+
When assigning a real-valued scalar to a complex number array (e.g., [`Complex128Array`][@stdlib/array/complex128] or [`Complex64Array`][@stdlib/array/complex64]), a fancy array will cast the real-valued scalar to a complex number argument having an imaginary component equal to zero.
265+
266+
```javascript
267+
var Complex128Array = require( '@stdlib/array/complex128' );
268+
var real = require( '@stdlib/complex/real' );
269+
var imag = require( '@stdlib/complex/imag' );
270+
271+
var x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ] );
272+
var y = array2fancy( x );
273+
// returns <Complex128Array>
274+
275+
// Retrieve the first element:
276+
var v = y[ 0 ];
277+
// returns <Complex128>
278+
279+
var re = real( v );
280+
// returns 1.0
281+
282+
var im = imag( v );
283+
// returns 2.0
284+
285+
// Assign a real-valued scalar to the first element:
286+
y[ 0 ] = 9.0;
287+
288+
v = y[ 0 ];
289+
// returns <Complex128>
290+
291+
re = real( v );
292+
// returns 9.0
293+
294+
im = imag( v );
295+
// returns 0.0
296+
```
297+
298+
Note, however, that attempting to assign a real-valued array to a complex number array slice is **not** supported due to the ambiguity of whether the real-valued array is a collection of real components (with implied imaginary components equal to zero) or an array of interleaved real and imaginary components.
299+
300+
<!-- run throws: true -->
301+
302+
```javascript
303+
var Float64Array = require( '@stdlib/array/float64' );
304+
var Complex128Array = require( '@stdlib/array/complex128' );
305+
306+
var x = new Complex128Array( [ 1.0, 2.0, 3.0, 4.0 ] );
307+
var y = array2fancy( x );
308+
// returns <Complex128Array>
309+
310+
// Attempt to assign a real-valued array:
311+
y[ ':' ] = new Float64Array( [ 5.0, 6.0 ] ); // is this a single complex number which should be broadcast or a list of real components with implied imaginary components?
312+
// throws <Error>
313+
```
314+
315+
</section>
316+
317+
<!-- /.notes -->
318+
319+
<!-- Package usage examples. -->
320+
321+
<section class="examples">
322+
323+
* * *
324+
325+
## Examples
326+
327+
<!-- eslint no-undef: "error" -->
328+
329+
```javascript
330+
var array2fancy = require( '@stdlib/array/to-fancy' );
331+
332+
var x = [ 1, 2, 3, 4, 5, 6 ];
333+
var y = array2fancy( x );
334+
// returns <Array>
335+
336+
var z = y[ '1::2' ];
337+
// returns [ 2, 4, 6 ]
338+
339+
z = y[ '-2::-2' ];
340+
// returns [ 5, 3, 1 ]
341+
342+
z = y[ '1:4' ];
343+
// returns [ 2, 3, 4 ]
344+
345+
y[ '4:1:-1' ] = 10;
346+
z = y[ ':' ];
347+
// returns [ 1, 2, 10, 10, 10, 6 ]
348+
349+
y[ '2:5' ] = [ -10, -9, -8 ];
350+
z = y[ ':' ];
351+
// returns [ 1, 2, -10, -9, -8, 6 ]
352+
```
353+
354+
</section>
355+
356+
<!-- /.examples -->
357+
358+
<!-- Section to include cited references. If references are included, add a horizontal rule *before* the section. Make sure to keep an empty line after the `section` element and another before the `/section` close. -->
359+
360+
<section class="references">
361+
362+
</section>
363+
364+
<!-- /.references -->
365+
366+
<!-- Section for related `stdlib` packages. Do not manually edit this section, as it is automatically populated. -->
367+
368+
<section class="related">
369+
370+
</section>
371+
372+
<!-- /.related -->
373+
374+
<!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. -->
375+
376+
<section class="links">
377+
378+
[@stdlib/repl]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/repl
379+
380+
[@stdlib/proxy/ctor]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/proxy/ctor
381+
382+
[@stdlib/slice/ctor]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/slice/ctor
383+
384+
[@stdlib/slice/seq2slice]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/slice/seq2slice
385+
386+
[@stdlib/ndarray/ctor]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/ndarray/ctor
387+
388+
[@stdlib/ndarray/base/broadcast-shapes]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/ndarray/base/broadcast-shapes
389+
390+
[@stdlib/array/mostly-safe-casts]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/array/mostly-safe-casts
391+
392+
[@stdlib/array/complex128]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/array/complex128
393+
394+
[@stdlib/array/complex64]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/array/complex64
395+
396+
</section>
397+
398+
<!-- /.links -->

0 commit comments

Comments
 (0)