Skip to content

Instantly share code, notes, and snippets.

@bigmistqke
Last active December 11, 2023 09:56
Show Gist options
  • Save bigmistqke/69ea9bad0784bfe297d821c6db982468 to your computer and use it in GitHub Desktop.
Save bigmistqke/69ea9bad0784bfe297d821c6db982468 to your computer and use it in GitHub Desktop.
experiments with mutable array-methods
class ArrayInPlace<T = number> extends Array {
commands: Command<T>[] = [];
constructor(values: T[]) {
super();
for (let i = 0; i < Math.floor(values.length / 100_000); i++) {
Array.prototype.push.apply(
this,
values.slice(i * 100_000, (i + 1) * 100_000),
);
}
}
mapInPlace(fn: (value: T, index: number) => T) {
for (let index = 0; index < this.length; index++) {
this[index] = fn(this[index], index);
}
return this;
}
filterInPlace(fn: (value: T, index: number) => boolean) {
let deleteCount = 0;
const length = this.length;
for (let index = length - 1; index >= 0; index--) {
if (!fn(this[index], index)) {
deleteCount++;
if (index === 0) {
Array.prototype.splice(index, deleteCount);
}
continue;
}
if (deleteCount > 0) {
this.splice(index, deleteCount);
}
}
return this;
}
set(index: number, value: T) {
this[index] = value;
return this;
}
}
const IS_MAP = Symbol();
const IS_FILTER = Symbol();
const IS_SET = Symbol();
type SetFn = (() => void) & { type?: typeof IS_SET };
type MapFn<T> = ((value: T, index: number) => T) & { type: typeof IS_MAP };
type FilterFn<T> = ((value: T, index: number) => boolean) & {
type: typeof IS_FILTER;
};
type Command<T> = MapFn<T> | SetFn | FilterFn<T>;
/**
* Array-like data structure with mutable, lazily executed methods.
*/
class LazyMutable<T = number> {
private _values: T[];
commands: Command<T>[] = [];
constructor(values: T[]) {
this._values = values;
}
private _run() {
for (const command of this.commands) {
switch (command.type) {
case IS_MAP:
for (let index = 0; index < this._values.length; index++) {
this._values[index] = command(this._values[index], index);
}
break;
case IS_FILTER:
let deleteCount = 0;
const length = this._values.length;
for (let index = length - 1; index >= 0; index--) {
if (!command(this._values[index], index)) {
deleteCount++;
if (index === 0) {
this._values.splice(index, deleteCount);
}
continue;
}
if (deleteCount > 0) {
this._values.splice(index, deleteCount);
}
}
break;
default:
command();
break;
}
}
this.commands = [];
}
/** lazily executed */
map(fn: (value: T, index: number) => T) {
const _fn = fn as MapFn<T>;
_fn.type = IS_MAP;
this.commands.push(_fn);
return this;
}
/** lazily executed */
filter(fn: (value: T, index: number) => boolean) {
const _fn = fn as FilterFn<T>;
_fn.type = IS_FILTER;
this.commands.push(_fn);
return this;
}
/** lazily executed */
set(index: number, value: T) {
const _fn = (() => void (this._values[index] = value)) as SetFn;
_fn.type = IS_SET;
this.commands.push(_fn);
return this;
}
/** immediately executed */
find = (fn: (value: T, index: number) => boolean) => (
this._run(), this._values.find(fn)
);
/** immediately executed */
sort = () => (this._run(), this._values.sort());
/** immediately executed */
forEach = (fn: (value: T, index: number) => void) => (
this._run(), this._values.forEach(fn)
);
/** immediately executed */
reduce = (
fn: (accumulator: T, value: T, index: number, values: T[]) => T,
) => (this._run(), this._values.reduce(fn));
/** immediately executed */
values = () => (this._run(), this._values);
/** immediately executed */
get = (index: number) => (this._run(), this._values[index])
}
class Mutable<T = number> {
private _values: T[];
commands: Command<T>[] = [];
constructor(values: T[]) {
this._values = values;
}
map(fn: (value: T, index: number) => T) {
for (let index = 0; index < this._values.length; index++) {
this._values[index] = fn(this._values[index], index);
}
return this;
}
filter(fn: (value: T, index: number) => boolean) {
let deleteCount = 0;
const length = this._values.length;
for (let index = length - 1; index >= 0; index--) {
if (!fn(this._values[index], index)) {
deleteCount++;
if (index === 0) {
Array.prototype.splice(index, deleteCount);
}
continue;
}
if (deleteCount > 0) {
this._values.splice(index, deleteCount);
}
}
return this;
}
set(index: number, value: T) {
this._values[index] = value;
return this;
}
find = (fn: (value: T, index: number) => boolean) => this._values.find(fn);
sort = () => this._values.sort();
forEach = (fn: (value: T, index: number) => void) => this._values.forEach(fn);
reduce = (fn: (accumulator: T, value: T, index: number, values: T[]) => T) =>
this._values.reduce(fn);
values = () => this._values;
get = (index: number) => this._values[index];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment