bst-typed
Version:
Binary Search Tree
353 lines (327 loc) • 11.9 kB
text/typescript
import type { ElementCallback, IterableElementBaseOptions, ReduceElementCallback } from '../../types';
/**
* Base class that makes a data structure iterable and provides common
* element-wise utilities (e.g., map/filter/reduce/find).
*
* @template E The public element type yielded by the structure.
* @template R The underlying "raw" element type used internally or by converters.
*
* @remarks
* This class implements the JavaScript iteration protocol (via `Symbol.iterator`)
* and offers array-like helpers with predictable time/space complexity.
*/
export abstract class IterableElementBase<E, R> implements Iterable<E> {
/**
* Create a new iterable base.
*
* @param options Optional behavior overrides. When provided, a `toElementFn`
* is used to convert a raw element (`R`) into a public element (`E`).
*
* @remarks
* Time O(1), Space O(1).
*/
protected constructor(options?: IterableElementBaseOptions<E, R>) {
if (options) {
const { toElementFn } = options;
if (typeof toElementFn === 'function') this._toElementFn = toElementFn;
else if (toElementFn) throw new TypeError('toElementFn must be a function type');
}
}
/**
* The converter used to transform a raw element (`R`) into a public element (`E`).
*
* @remarks
* Time O(1), Space O(1).
*/
protected _toElementFn?: (rawElement: R) => E;
/**
* Exposes the current `toElementFn`, if configured.
*
* @returns The converter function or `undefined` when not set.
* @remarks
* Time O(1), Space O(1).
*/
get toElementFn(): ((rawElement: R) => E) | undefined {
return this._toElementFn;
}
/**
* Returns an iterator over the structure's elements.
*
* @param args Optional iterator arguments forwarded to the internal iterator.
* @returns An `IterableIterator<E>` that yields the elements in traversal order.
*
* @remarks
* Producing the iterator is O(1); consuming the entire iterator is Time O(n) with O(1) extra space.
*/
*[Symbol.iterator](...args: unknown[]): IterableIterator<E> {
yield* this._getIterator(...args);
}
/**
* Returns an iterator over the values (alias of the default iterator).
*
* @returns An `IterableIterator<E>` over all elements.
* @remarks
* Creating the iterator is O(1); full iteration is Time O(n), Space O(1).
*/
*values(): IterableIterator<E> {
for (const item of this) yield item;
}
/**
* Tests whether all elements satisfy the predicate.
*
* @template TReturn
* @param predicate Function invoked for each element with signature `(value, index, self)`.
* @param thisArg Optional `this` binding for the predicate.
* @returns `true` if every element passes; otherwise `false`.
*
* @remarks
* Time O(n) in the worst case; may exit early when the first failure is found. Space O(1).
*/
every(predicate: ElementCallback<E, R, boolean>, thisArg?: unknown): boolean {
let index = 0;
for (const item of this) {
if (thisArg === undefined) {
if (!predicate(item, index++, this)) return false;
} else {
const fn = predicate as (this: unknown, v: E, i: number, self: this) => boolean;
if (!fn.call(thisArg, item, index++, this)) return false;
}
}
return true;
}
/**
* Tests whether at least one element satisfies the predicate.
*
* @param predicate Function invoked for each element with signature `(value, index, self)`.
* @param thisArg Optional `this` binding for the predicate.
* @returns `true` if any element passes; otherwise `false`.
*
* @remarks
* Time O(n) in the worst case; may exit early on first success. Space O(1).
*/
some(predicate: ElementCallback<E, R, boolean>, thisArg?: unknown): boolean {
let index = 0;
for (const item of this) {
if (thisArg === undefined) {
if (predicate(item, index++, this)) return true;
} else {
const fn = predicate as (this: unknown, v: E, i: number, self: this) => boolean;
if (fn.call(thisArg, item, index++, this)) return true;
}
}
return false;
}
/**
* Invokes a callback for each element in iteration order.
*
* @param callbackfn Function invoked per element with signature `(value, index, self)`.
* @param thisArg Optional `this` binding for the callback.
* @returns `void`.
*
* @remarks
* Time O(n), Space O(1).
*/
forEach(callbackfn: ElementCallback<E, R, void>, thisArg?: unknown): void {
let index = 0;
for (const item of this) {
if (thisArg === undefined) {
callbackfn(item, index++, this);
} else {
const fn = callbackfn as (this: unknown, v: E, i: number, self: this) => void;
fn.call(thisArg, item, index++, this);
}
}
}
/**
* Finds the first element that satisfies the predicate and returns it.
*
* @overload
* Finds the first element of type `S` (a subtype of `E`) that satisfies the predicate and returns it.
* @template S
* @param predicate Type-guard predicate: `(value, index, self) => value is S`.
* @param thisArg Optional `this` binding for the predicate.
* @returns The matched element typed as `S`, or `undefined` if not found.
*
* @overload
* @param predicate Boolean predicate: `(value, index, self) => boolean`.
* @param thisArg Optional `this` binding for the predicate.
* @returns The first matching element as `E`, or `undefined` if not found.
*
* @remarks
* Time O(n) in the worst case; may exit early on the first match. Space O(1).
*/
find<S extends E>(predicate: ElementCallback<E, R, S>, thisArg?: unknown): S | undefined;
find(predicate: ElementCallback<E, R, unknown>, thisArg?: unknown): E | undefined;
// Implementation signature
find(predicate: ElementCallback<E, R, boolean>, thisArg?: unknown): E | undefined {
let index = 0;
for (const item of this) {
if (thisArg === undefined) {
if (predicate(item, index++, this)) return item;
} else {
const fn = predicate as (this: unknown, v: E, i: number, self: this) => boolean;
if (fn.call(thisArg, item, index++, this)) return item;
}
}
return;
}
/**
* Checks whether a strictly-equal element exists in the structure.
*
* @param element The element to test with `===` equality.
* @returns `true` if an equal element is found; otherwise `false`.
*
* @remarks
* Time O(n) in the worst case. Space O(1).
*/
has(element: E): boolean {
for (const ele of this) if (ele === element) return true;
return false;
}
reduce(callbackfn: ReduceElementCallback<E, R>): E;
reduce(callbackfn: ReduceElementCallback<E, R>, initialValue: E): E;
reduce<U>(callbackfn: ReduceElementCallback<E, R, U>, initialValue: U): U;
/**
* Reduces all elements to a single accumulated value.
*
* @overload
* @param callbackfn Reducer of signature `(acc, value, index, self) => nextAcc`. The first element is used as the initial accumulator.
* @returns The final accumulated value typed as `E`.
*
* @overload
* @param callbackfn Reducer of signature `(acc, value, index, self) => nextAcc`.
* @param initialValue The initial accumulator value of type `E`.
* @returns The final accumulated value typed as `E`.
*
* @overload
* @template U The accumulator type when it differs from `E`.
* @param callbackfn Reducer of signature `(acc: U, value, index, self) => U`.
* @param initialValue The initial accumulator value of type `U`.
* @returns The final accumulated value typed as `U`.
*
* @remarks
* Time O(n), Space O(1). Throws if called on an empty structure without `initialValue`.
*/
reduce<U>(callbackfn: ReduceElementCallback<E, R, U>, initialValue?: U): U {
let index = 0;
const iter = this[Symbol.iterator]();
let acc: U;
if (arguments.length >= 2) {
acc = initialValue as U;
} else {
const first = iter.next();
if (first.done) throw new TypeError('Reduce of empty structure with no initial value');
acc = first.value as unknown as U;
index = 1;
}
for (const value of iter as unknown as Iterable<E>) {
acc = callbackfn(acc, value, index++, this);
}
return acc;
}
/**
* Materializes the elements into a new array.
*
* @returns A shallow array copy of the iteration order.
* @remarks
* Time O(n), Space O(n).
*/
toArray(): E[] {
return [...this];
}
/**
* Returns a representation of the structure suitable for quick visualization.
* Defaults to an array of elements; subclasses may override to provide richer visuals.
*
* @returns A visual representation (array by default).
* @remarks
* Time O(n), Space O(n).
*/
toVisual(): E[] {
return [...this];
}
/**
* Prints `toVisual()` to the console. Intended for quick debugging.
*
* @returns `void`.
* @remarks
* Time O(n) due to materialization, Space O(n) for the intermediate representation.
*/
print(): void {
console.log(this.toVisual());
}
/**
* Indicates whether the structure currently contains no elements.
*
* @returns `true` if empty; otherwise `false`.
* @remarks
* Expected Time O(1), Space O(1) for most implementations.
*/
abstract isEmpty(): boolean;
/**
* Removes all elements from the structure.
*
* @returns `void`.
* @remarks
* Expected Time O(1) or O(n) depending on the implementation; Space O(1).
*/
abstract clear(): void;
/**
* Creates a structural copy with the same element values and configuration.
*
* @returns A clone of the current instance (same concrete type).
* @remarks
* Expected Time O(n) to copy elements; Space O(n).
*/
abstract clone(): this;
/**
* Maps each element to a new element and returns a new iterable structure.
*
* @template EM The mapped element type.
* @template RM The mapped raw element type used internally by the target structure.
* @param callback Function with signature `(value, index, self) => mapped`.
* @param options Optional options for the returned structure, including its `toElementFn`.
* @param thisArg Optional `this` binding for the callback.
* @returns A new `IterableElementBase<EM, RM>` containing mapped elements.
*
* @remarks
* Time O(n), Space O(n).
*/
abstract map<EM, RM>(
callback: ElementCallback<E, R, EM>,
options?: IterableElementBaseOptions<EM, RM>,
thisArg?: unknown
): IterableElementBase<EM, RM>;
/**
* Maps each element to the same element type and returns the same concrete structure type.
*
* @param callback Function with signature `(value, index, self) => mappedValue`.
* @param thisArg Optional `this` binding for the callback.
* @returns A new instance of the same concrete type with mapped elements.
*
* @remarks
* Time O(n), Space O(n).
*/
abstract mapSame(callback: ElementCallback<E, R, E>, thisArg?: unknown): this;
/**
* Filters elements using the provided predicate and returns the same concrete structure type.
*
* @param predicate Function with signature `(value, index, self) => boolean`.
* @param thisArg Optional `this` binding for the predicate.
* @returns A new instance of the same concrete type containing only elements that pass the predicate.
*
* @remarks
* Time O(n), Space O(k) where `k` is the number of kept elements.
*/
abstract filter(predicate: ElementCallback<E, R, boolean>, thisArg?: unknown): this;
/**
* Internal iterator factory used by the default iterator.
*
* @param args Optional iterator arguments.
* @returns An iterator over elements.
*
* @remarks
* Implementations should yield in O(1) per element with O(1) extra space when possible.
*/
protected abstract _getIterator(...args: unknown[]): IterableIterator<E>;
}