Skip to content

Commit a68003e

Browse files
authored
Factor out ArraySlice into its own TypeScript goody (#162)
I would like to use the `ArraySlice` class to do some work with combinations, permutations, and subsets. It will be nice to have it as a separate goody for this purpose.
1 parent 904bb33 commit a68003e

File tree

4 files changed

+331
-178
lines changed

4 files changed

+331
-178
lines changed
Lines changed: 4 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,25 @@
1-
type ArraySliceProxyHandler<T> = {
2-
get(
3-
target: ArraySlice<T>,
4-
property: string | symbol,
5-
receiver: ArraySlice<T>,
6-
): unknown;
7-
};
8-
9-
class ArraySlice<T> {
10-
private constructor(
11-
public readonly array: ReadonlyArray<T>,
12-
public readonly start: number,
13-
public readonly end: number,
14-
) {}
15-
16-
get length(): number {
17-
return this.end - this.start + 1;
18-
}
19-
20-
at(index: number): T | undefined {
21-
const adjustedIndex = index < 0 ? index + this.length : index;
22-
return this.array[this.start + adjustedIndex];
23-
}
24-
25-
*[Symbol.iterator](this: ArraySlice<T>): Generator<T, void, void> {
26-
for (let i = this.start; i <= this.end; ++i) {
27-
yield this.array[i];
28-
}
29-
}
30-
31-
slide(delta: number = 1): IndexableArraySlice<T> {
32-
return ArraySlice.get(this.array, this.start + delta, this.end + delta);
33-
}
34-
35-
isPrefix(): boolean {
36-
return this.start === 0;
37-
}
38-
39-
isSuffix(): boolean {
40-
return this.end === this.array.length - 1;
41-
}
42-
43-
private declare static proxyHandler?: ArraySliceProxyHandler<unknown>;
44-
45-
static get<T>(
46-
array: ReadonlyArray<T>,
47-
start: number,
48-
end: number,
49-
): IndexableArraySlice<T> {
50-
ArraySlice.proxyHandler ??= {
51-
get(
52-
target: ArraySlice<unknown>,
53-
property: string | symbol,
54-
receiver: ArraySlice<unknown>,
55-
): unknown {
56-
if (typeof property === "string") {
57-
const index = parseInt(property, 10);
58-
if (String(index) === property) {
59-
if (index < 0 || index >= receiver.length) {
60-
return undefined;
61-
}
62-
return receiver.at(index);
63-
}
64-
}
65-
66-
return (target as unknown as Record<string | symbol, unknown>)[
67-
property
68-
];
69-
},
70-
};
71-
72-
return new Proxy(
73-
new ArraySlice(array, start, end),
74-
ArraySlice.proxyHandler as ArraySliceProxyHandler<T>,
75-
) as IndexableArraySlice<T>;
76-
}
77-
}
78-
79-
type IndexableArraySlice<T> = ArraySlice<T> & {
80-
[index: number]: T | undefined;
81-
};
1+
import { type IndexableArraySlice, ArraySlice } from "../ArraySlice";
822

833
declare global {
844
interface ReadonlyArray<T> {
855
slidingWindows(
866
this: ReadonlyArray<T>,
877
windowSize: number,
88-
): Generator<ArraySlice<T>, void, void>;
8+
): Generator<IndexableArraySlice<T>, void, void>;
899
}
9010

9111
interface Array<T> {
9212
slidingWindows(
9313
this: ReadonlyArray<T>,
9414
windowSize: number,
95-
): Generator<ArraySlice<T>, void, void>;
15+
): Generator<IndexableArraySlice<T>, void, void>;
9616
}
9717
}
9818

9919
Array.prototype.slidingWindows = function* <T>(
10020
this: ReadonlyArray<T>,
10121
windowSize: number,
102-
): Generator<ArraySlice<T>, void, void> {
22+
): Generator<IndexableArraySlice<T>, void, void> {
10323
for (
10424
let win: IndexableArraySlice<T> | null = ArraySlice.get(
10525
this,
@@ -112,7 +32,3 @@ Array.prototype.slidingWindows = function* <T>(
11232
yield win;
11333
}
11434
};
115-
116-
// Needed to fix the error "Augmentations for the global scope can only be directly nested in external modules or ambient module declarations. ts(2669)"
117-
// See: https://stackoverflow.com/questions/57132428/augmentations-for-the-global-scope-can-only-be-directly-nested-in-external-modul
118-
export {};
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
type ArraySliceProxyHandler<T> = {
2+
get(
3+
target: ArraySlice<T>,
4+
property: PropertyKey,
5+
receiver: ArraySlice<T>,
6+
): unknown;
7+
};
8+
9+
export type IndexableArraySlice<T> = ArraySlice<T> & {
10+
[index: number]: T | undefined;
11+
};
12+
13+
export class ArraySlice<T> {
14+
private constructor(
15+
public readonly array: ReadonlyArray<T>,
16+
public readonly start: number,
17+
public readonly end: number,
18+
) {}
19+
20+
get length(): number {
21+
return this.end - this.start + 1;
22+
}
23+
24+
at(index: number): T | undefined {
25+
const adjustedIndex = index < 0 ? index + this.length : index;
26+
return this.array[this.start + adjustedIndex];
27+
}
28+
29+
*[Symbol.iterator](this: ArraySlice<T>): Generator<T, void, void> {
30+
for (let i = this.start; i <= this.end; ++i) {
31+
yield this.array[i];
32+
}
33+
}
34+
35+
slide(delta: number = 1): IndexableArraySlice<T> {
36+
return ArraySlice.get(this.array, this.start + delta, this.end + delta);
37+
}
38+
39+
isPrefix(): boolean {
40+
return this.start === 0;
41+
}
42+
43+
isSuffix(): boolean {
44+
return this.end === this.array.length - 1;
45+
}
46+
47+
private declare static proxyHandler?: ArraySliceProxyHandler<unknown>;
48+
49+
static get<T>(
50+
array: ReadonlyArray<T>,
51+
start: number,
52+
end: number,
53+
): IndexableArraySlice<T> {
54+
ArraySlice.proxyHandler ??= {
55+
get(
56+
target: ArraySlice<unknown>,
57+
property: PropertyKey,
58+
receiver: ArraySlice<unknown>,
59+
): unknown {
60+
if (typeof property === "string" || typeof property === "number") {
61+
const index = parseInt(String(property), 10);
62+
if (String(index) === String(property)) {
63+
if (index < 0 || index >= receiver.length) {
64+
return undefined;
65+
}
66+
return receiver.at(index);
67+
}
68+
}
69+
70+
return (target as unknown as Record<PropertyKey, unknown>)[property];
71+
},
72+
};
73+
74+
return new Proxy(
75+
new ArraySlice(array, start, end),
76+
ArraySlice.proxyHandler as ArraySliceProxyHandler<T>,
77+
) as IndexableArraySlice<T>;
78+
}
79+
}

0 commit comments

Comments
 (0)