Skip to content

Instantly share code, notes, and snippets.

@devlato
Created September 18, 2023 04:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devlato/c1d4ca0250a8cd64b5f1adf4aa1093aa to your computer and use it in GitHub Desktop.
Save devlato/c1d4ca0250a8cd64b5f1adf4aa1093aa to your computer and use it in GitHub Desktop.
ArrayView for JS Arrays
// `ReadonlyArrayView` class allows to avoid copying when creating array slices
// It can be used as a drop-in replacement instead of an array but only gives read access to the original array
// Worth remembering that this class doesn't track index change, element reordering, etc., so use it at your own risk
//
// Demo:
// https://www.typescriptlang.org/play?module=1#code/C4TwDgpgBASghgOwObQLxQN4FgBQUoBmATgPYC2AkggCYQAeAXFAgK5kBGERA3LvsCSq1GzNpx64Avrxy56YEkWBQAxgBs4AZ02wIcaiQRqQAQSJE4IAGoBLCAHcAPABUAfFBtkwaiGQgJgHTMLEAAZGwBrCBd3bDwoIj0DIxAoAG0EJlYOLgBdJmdcPigwIhsANzhgaBVDTWAiFhUBIgAKUoqq6ET9Q2MoOHNLArTcgBoSssrqhKS+1ItkCCZ4JYBKTEki+PqqmxUoEgIY1sGQkfGExBQV64g1lbmU4MtbBxjMYvxahHqocrs9ig6AQDl0vWeQ2sgJOZ0sE0WKDWMi+qjqygQhgAwnA1Bp2D5gVBWhtULFUfwABakIGgoEAUXMilaAHIcQhMcoVLi1FBgJSbDotANCCwEM0bIYWcjUdJtvh8IlgCwiAhmGCAAqkOggVoAhwTOIKhVwMDeEBZbE8uAEiBjClo34NJrAS0IHF4m0+e3xY20Ag2UFakiQJQW5hWz22n3G-C0HzVYOh0Buj3470Okj2BAAaQgIE0TBJwPcaRZPmQ-JZExZUQL1agLMqahYEE0DZZAgAyg1A0gWeMHShXcTgINhxMwBMAPqk8m+2M2AjEsAeNVjojDjZG2PGpUq9fjiDANKr4WIEC5GS7-BbWQL41LldrvlH4AAOjhIG3Dr3x4Pr6bsen5QqeAxCggl7Xjed6-rMyqqlAYr+oGEDUNBxqSDGsaUloRYbhOJRzp8D4KvuiGroGgHDlAAA+tElC+BHAV+GEKlhDqCvSdDVL8Ni2kWxEELimh2g6pQQOU-jANxvGaJKvyCSWfKNGJpGicAwYCKAkAAPIEKm1rRrKMrxLB8Q0RWSD8sWO6Kv+iH8oK76IhA77EOQQj0FAAC0fICpoLl3O+AheXQ0HmfgABUdaaLZqIEIoxIJh4RJOYFrnuaQlA0PQ3CpY4-nOZloW5eFHgANQVT+pEgHYajUB4bF3re8pQFFzatnFNXGolRDJceqVHEVgWxSSPWxnVEANSNIEhGkNhXrKxSRXyJA9mUyDxQu5Fqmk74Hel76dW2JK5CF629ltpmtfe-AkAAUl2ukAHLbX+CFqk9r3vvUm1IEuur7YdAXHbiXVnTdUCrVFaRdiAHAkGo742NUFgtLk70Kn1A3KDYhzLkdY1rBNxpTTNR1fgtS0Li10NSNsPx-GcjwQsYLzQu8f19u46AwE87NQm89jvkcrRliYHYAEIdliHYACIdvSHYAGIdgA4h2AASA6GoQ2VhUwABMEylcITAACzQ6ZTPKGcAAMRJnGk9s00zSNuWoJBIKyDsNg7pk460KX48NZwTe7Pjvl7PsAAbOwAJBgNiSLkscTDYpl3pHnve775jvsTDbA3Nhf5t1uQ23UHvR3nLJnGDLanWsxcHaXJ0V1Xvw1zH+dEBdG19iS-sF92V0+yTMg57XPsst9L2-VdAZILqLcTPPi--YDpzmJPuBAA
export class ReadonlyArrayView<T> implements ArrayLike<T> {
readonly [n: number]: T
private constructor(private readonly array: T[], private readonly range: Range) {}
static of<T>(array: T[], range: Range): ReadonlyArrayView<T> {
const view = new ReadonlyArrayView<T>(array, range);
const nonCallable = () => {
throw new Error('Cannot call this as a function');
};
return new Proxy(view, {
apply: nonCallable,
construct: nonCallable,
defineProperty: nonCallable,
deleteProperty: nonCallable,
ownKeys: () => ['length', 'keys', 'values', 'toString'],
get: (target, p, _) => {
if (p in target) {
return target[p as any];
}
if (p in target.array) {
return target.array[p as any];
}
return undefined;
},
has: (target, p) => {
return p in target || p in target.array;
},
isExtensible: () => false,
preventExtensions: () => true,
setPrototypeOf: nonCallable,
});
}
get length() {
return this.range.fromIndex - this.range.toIndex;
}
*keys() {
for (let i = this.range.fromIndex; i < this.range.toIndex; i++) {
yield i;
}
}
*values() {
for (const i of this.keys()) {
yield this.array[i];
}
}
toString() {
return [...this.values()].toString();
}
toJSON() {
return JSON.stringify([...this.values()]);
}
*[Symbol.iterator]() {
for (const i of this.keys()) {
yield this.array[i];
}
}
}
type Range = {
fromIndex: number,
toIndex: number,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment