Skip to content

Commit

Permalink
Merge 62a53b9 into 0936c38
Browse files Browse the repository at this point in the history
  • Loading branch information
Smoren committed Mar 10, 2024
2 parents 0936c38 + 62a53b9 commit fb9f76a
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const originalView = view(originalArray);
originalView.subview(mask([true, false, true, false, true])).toArray(); // [1, 3, 5]
originalView.subview(select([1, 2, 4])).toArray(); // [2, 3, 5]
originalView.subview(slice('::-1')).toArray(); // [5, 4, 3, 2, 1]
originalView.subview(slice([,,-1])).toArray(); // [5, 4, 3, 2, 1]

originalView.subview(mask([true, false, true, false, true])).apply((x: number) => x * 10);
originalArray; // [10, 2, 30, 4, 50]
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "array-view",
"version": "0.1.7",
"version": "0.2.0",
"description": "Create array views for easy data manipulation, select elements using Python-like slice notation, enable efficient selection of elements using index lists and boolean masks.",
"license": "MIT",
"repository": {
Expand Down
4 changes: 2 additions & 2 deletions src/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function view<T>(source: Array<T> | ArrayViewInterface<T>, readonly?: boo
*
* This function allows you to create a selector for defining a subset of elements based on a slice range.
*
* @param {string | Slice} slice - The slice string or Slice object to create the selector from.
* @param {string | Array<number | undefined> | Slice} slice - The slice string/array or Slice object to create the selector from.
*
* @returns {SliceSelector} The created SliceSelector instance.
*
Expand All @@ -52,7 +52,7 @@ export function view<T>(source: Array<T> | ArrayViewInterface<T>, readonly?: boo
* const slicedArray = view.loc[sliceSelector.toString()];
* console.log(slicedArray);
* // [2, 3, 4] */
export function slice(slice: string | Slice): SliceSelector {
export function slice(slice: string | Array<number | undefined> | Slice): SliceSelector {
return new SliceSelector(Slice.toSlice(slice));
}

Expand Down
37 changes: 33 additions & 4 deletions src/structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,23 @@ export class Slice {
/**
* Converts a slice string or Slice object into a Slice instance.
*
* @param {string | Slice} s - The slice string or Slice object to convert.
* @param {string | Array<number | undefined> | Slice} s - The slice string/array or Slice object to convert.
* @returns {Slice} The converted Slice instance.
*/
public static toSlice(s: string | Slice): Slice {
public static toSlice(s: string | Array<number | undefined> | Slice): Slice {
if (s instanceof Slice) {
return s;
}

if (Array.isArray(s) && this.isSliceArray(s)) {
return new Slice(...(s as Array<number | undefined>));
}

if (!this.isSliceString(s)) {
throw new ValueError(`Invalid slice: "${String(s)}".`);
}

const slice = this.parseSliceString(s);
const slice = this.parseSliceString(s as string);

return new Slice(...slice);
}
Expand Down Expand Up @@ -71,7 +75,32 @@ export class Slice {

const slice = this.parseSliceString(s);

return !(slice.length < 1 || slice.length > 3);
return slice.length >= 1 && slice.length <= 3;
}

/**
* Checks if the provided value is a valid slice array.
*
* @param {unknown} s - The value to check.
*
* @returns {boolean} True if the value is a valid slice array, false otherwise.
*/
public static isSliceArray(s: unknown): boolean {
if (!Array.isArray(s)) {
return false;
}

if(!(s.length >= 0 && s.length <= 3)) {
return false;
}

for (const item of s) {
if (item !== undefined && !Number.isInteger(item)) {
return false;
}
}

return true;
}

/**
Expand Down
1 change: 1 addition & 0 deletions tests/examples/examples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ it("Subview example", () => {
expect(originalView.subview(mask([true, false, true, false, true])).toArray()).toEqual([1, 3, 5]);
expect(originalView.subview(select([1, 2, 4])).toArray()).toEqual([2, 3, 5]);
expect(originalView.subview(slice('::-1')).toArray()).toEqual([5, 4, 3, 2, 1]);
expect(originalView.subview(slice([,,-1])).toArray()).toEqual([5, 4, 3, 2, 1]);

originalView.subview(mask([true, false, true, false, true])).apply((x: number) => x * 10);
expect(originalArray).toEqual([10, 2, 30, 4, 50]);
Expand Down
64 changes: 64 additions & 0 deletions tests/structs/slice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,67 @@ function dataProviderForNormalize(): Array<unknown> {
[':3:-1', 10, '9:3:-1', [9, 8, 7, 6, 5, 4]],
];
}

describe.each([
...dataProviderForIsSliceArrayTrue(),
] as Array<[Array<number>]>)(
"Is Slice Array",
(input: Array<number>) => {
it("", () => {
expect(Slice.isSliceArray(input)).toBeTruthy();
});
},
);

function dataProviderForIsSliceArrayTrue(): Array<unknown> {
return [
[[]],
[[,]],
[[,,]],
[[0]],
[[0,]],
[[0,,]],
[[1,,]],
[[1,0,]],
[[1,1,]],
[[-1,1,]],
[[1,,1]],
[[1,,2]],
[[,,1]],
[[,,-1]],
[[1,10,-1]],
];
}

describe.each([
...dataProviderForIsSliceArrayFalse(),
] as Array<[Array<number>]>)(
"Is Slice Array",
(input: Array<number>) => {
it("", () => {
expect(Slice.isSliceArray(input)).toBeFalsy();
});
},
);

function dataProviderForIsSliceArrayFalse(): Array<unknown> {
return [
[['']],
[['a']],
[[0, 1, 'a']],
[[0, 1, 2, 3]],
[[1.1, 1, 2]],
[[1, 1, 2.2]],
[null],
[0],
[1],
[0.0],
[1.0],
[true],
[false],
[{}],
[{a: 1}],
[[{}]],
[[{a: 1}]],
];
}
94 changes: 93 additions & 1 deletion tests/views/slice-view.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
dataProviderForSliceOutOfBoundsPythonAutoGenerated,
// @ts-ignore
} from "../fixtures/python-slices";
import { view } from "../../src";
import { slice, view } from "../../src";

describe.each([
...dataProviderForSliceSubviewRead(),
Expand Down Expand Up @@ -76,6 +76,40 @@ describe.each([
},
);


describe.each([
...dataProviderForSliceSubviewRead(),
...dataProviderForSliceArraySubviewRead(),
] as Array<[Array<number>, string | Array<number | undefined>, Array<number>]>)(
"Array View Slice Array Subview Loc Read Test",
(
source: Array<number>,
config: string | Array<number | undefined>,
expected: Array<number>,
) => {
it("", () => {
// Given
const v = view(source);
const slicedArray = v.subview(slice(config)).loc[':'];

expect(slicedArray).toEqual(expected);
expect(slicedArray.length).toEqual(expected.length);

for (let i = 0; i < slicedArray.length; i++) {
expect(slicedArray[i]).toBe(expected[i]);
}

for (let i = 0; i < v.length; i++) {
expect(v.loc[i]).toBe(source[i]);
}

// And then
expect(v.toArray()).toEqual(source);
expect(slicedArray).toEqual(expected);
});
},
);

function dataProviderForSliceSubviewRead(): Array<unknown> {
return [
[[1, 2, 3, 4, 5, 6, 7, 8, 9], '1:6', [2, 3, 4, 5, 6]],
Expand Down Expand Up @@ -129,6 +163,17 @@ function dataProviderForSliceSubviewRead(): Array<unknown> {
];
}

function dataProviderForSliceArraySubviewRead(): Array<unknown> {
return [
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [1,6], [2, 3, 4, 5, 6]],
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [1,6,1], [2, 3, 4, 5, 6]],
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [1,6,2], [2, 4, 6]],
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [2,8], [3, 4, 5, 6, 7, 8]],
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [2,8,1], [3, 4, 5, 6, 7, 8]],
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [2,8,2], [3, 5, 7]],
];
}

describe.each([
...dataProviderForSliceSubviewWrite(),
] as Array<[Array<number>, string, Array<number>, Array<number>]>)(
Expand All @@ -153,6 +198,31 @@ describe.each([
},
);

describe.each([
...dataProviderForSliceSubviewWrite(),
...dataProviderForSliceArraySubviewWrite(),
] as Array<[Array<number>, string | Array<number | undefined>, Array<number>, Array<number>]>)(
"Array View Slice Subview Loc Write Test",
(
source: Array<number>,
config: string | Array<number | undefined>,
toWrite: Array<number>,
expected: Array<number>,
) => {
it("", () => {
// Given
const v = view(source);

// When
v.subview(slice(config)).loc[':'] = toWrite;

// And then
expect(v.toArray()).toEqual(expected);
expect(source).toEqual(expected);
});
},
);

function dataProviderForSliceSubviewWrite(): Array<unknown> {
return [
[[], ':', [], []],
Expand All @@ -173,3 +243,25 @@ function dataProviderForSliceSubviewWrite(): Array<unknown> {
[[1, 2, 3, 4, 5, 6, 7, 8], '1:-1:2', [77, 88, 99], [1, 77, 3, 88, 5, 99, 7, 8]],
];
}

function dataProviderForSliceArraySubviewWrite(): Array<unknown> {
return [
[[], [], [], []],
[[], [,], [], []],
[[1], [,], [11], [11]],
[[1, 2, 3], [,], [2, 4, 6], [2, 4, 6]],
[[1, 2, 3], [0,], [2, 4, 6], [2, 4, 6]],
[[1, 2, 3], [0,3], [2, 4, 6], [2, 4, 6]],
[[1, 2, 3], [0,3], [2, 4, 6], [2, 4, 6]],
[[1, 2, 3], [1,], [22, 33], [1, 22, 33]],
[[1, 2, 3], [,2], [11, 22], [11, 22, 3]],
[[1, 2, 3], [,-1], [11, 22], [11, 22, 3]],
[[1, 2, 3, 4, 5, 6], [,,2], [77, 88, 99], [77, 2, 88, 4, 99, 6]],
[[1, 2, 3, 4, 5, 6], [,,-2], [77, 88, 99], [1, 99, 3, 88, 5, 77]],
[[1, 2, 3, 4, 5, 6], [1,,2], [77, 88, 99], [1, 77, 3, 88, 5, 99]],
[[1, 2, 3, 4, 5, 6], [-2,,-2], [77, 88, 99], [99, 2, 88, 4, 77, 6]],
[[1, 2, 3, 4, 5, 6, 7, 8], [,-2,2], [77, 88, 99], [77, 2, 88, 4, 99, 6, 7, 8]],
[[1, 2, 3, 4, 5, 6, 7, 8], [,6,2], [77, 88, 99], [77, 2, 88, 4, 99, 6, 7, 8]],
[[1, 2, 3, 4, 5, 6, 7, 8], [1,-1,2], [77, 88, 99], [1, 77, 3, 88, 5, 99, 7, 8]],
];
}

0 comments on commit fb9f76a

Please sign in to comment.