indexable-array
Version:
Extended native JavaScript Array which provides indexed lookup similar to native Map.
314 lines • 19.5 kB
TypeScript
declare type AvailableDefaultIndex<U, DK, OK> = DK extends keyof U ? DK : Extract<OK, keyof U>;
declare type AvailableIndex<U, DK, OK> = Exclude<Extract<OK, keyof U>, AvailableDefaultIndex<U, DK, OK>>;
declare type Callback<I, DK extends keyof I, OK extends keyof I, TH extends boolean, R> = (value: I, index: number, array: IndexableArray<I, DK, OK, TH>) => R;
declare type CallbackThis<I, DK extends keyof I, OK extends keyof I, TH extends boolean, R, T = undefined> = (this: T, value: I, index: number, array: IndexableArray<I, DK, OK, TH>) => R;
/**
* Extended native array class to access array elements by fast key lookups using binary search. Used for storing objects.
*
* @example
* import IndexableArray, { Self } from "indexable-array";
* const users = new IndexableArray({ id: 23, name: "Geroge" }, { id: 96, name: "Lisa" }).addIndex("name");
* Array.isArray(users); // true
* users.get("George"); // { id: 23, name: "George"}
* const user = { id: 21, name: "Henry" };
* users[0] = user;
* users.getIndex(user); // 0 - It is possible to index whole object by { selfIndex: true } option.
* users.splice(1, 1, { id: 34, name: "Henry" });
* users.getAllIndexes("Henry"); // [0, 1];
*
* users[0].name = "DON'T DO THIS"; // WRONG: Sub fields (i.e. [0]."name") of the array is not watched, so index does not get updated.
* users.set(0, "name", "OK"); // Index updated.
* users.disableIndex();
* users[0].name = "THIS IS OK NOW";
* users.enableIndex(); // Index is recreated from scratch.
*/
export default class IndexableArray<I extends any, DK extends keyof I, OK extends keyof I, TH extends boolean = false> extends Array<I> {
private readonly primitiveLookups;
private readonly objectLookups;
private readonly builtIndexKeys;
private _defaultKey?;
private indexEnabled;
private operationAtEnd;
private _throwUnknown;
/**
* Set of the indexed key names. `$$self` is used for the whole value.
*
* @example
* const users = new IndexableArray({ id: 23, name: "Geroge" }, { id: 96, name: "Lisa" }).addSelfIndex().addIndex("name");
* users.indexedArray; // ["$$self", "name"]
*/
readonly indexedKeys: Set<DK | OK>;
/**
* Creates an `IndexableArray` instance from given items.
*
* @param items are items to create `IndexableArray` from.
*/
private constructor();
static from<I2 extends any, DK2 extends keyof I2, DK3 extends keyof I2 = DK2, OK2 extends keyof I2 = never, OK3 extends keyof I2 = OK2, TH2 extends boolean = false>(indexableArray: IndexableArray<I2, DK2, OK2, TH2>, defaultKey?: DK3, ...indexKeys: OK3[]): IndexableArray<I2, DK3, Exclude<OK3, DK3>, TH2>;
static from<I2, DK2 extends keyof I2, OK2 extends keyof I2 = never>(arrayLike: Iterable<I2> | ArrayLike<I2>, defaultKey: DK2, ...indexKeys: OK2[]): IndexableArray<I2, DK2, Exclude<OK2, DK2>, false>;
static throwingFrom<I2 extends any, DK2 extends keyof I2, DK3 extends keyof I2 = DK2, OK2 extends keyof I2 = never, OK3 extends keyof I2 = OK2, TH2 extends boolean = false>(indexableArray: IndexableArray<I2, DK2, OK2, TH2>, defaultKey?: DK3, ...indexKeys: OK3[]): IndexableArray<I2, DK3, Exclude<OK3, DK3>, true>;
static throwingFrom<I2, DK2 extends keyof I2, OK2 extends keyof I2 = never>(arrayLike: Iterable<I2> | ArrayLike<I2>, defaultKey: DK2, ...indexKeys: OK2[]): IndexableArray<I2, DK2, Exclude<OK2, DK2>, true>;
private get defaultKey();
/**
* Clears index by emptying related index fields.
*
* @ignore
*/
private clearIndex;
/**
* Adds given keys to the index.
*
* @ignore
* @param keys are list of keys to add to index.
* @returns this object.
*/
private addIndex;
/**
* Adds same index types from another IndexableArray.
*
* @ignore
* @param source is `IndexableArray` to get index keys from.
* @returns this object.
* @example
* const users = new IndexableArray({ id: 23, name: "Geroge" }, { id: 96, name: "Lisa" }).addIndex("name");
* const other = new IndexableArray().addIndexFrom(users); // Indexes "name".
*/
_copyMeta<I2 extends any, DK2 extends DK & keyof I2, OK2 extends OK & keyof I2>(source: IndexableArray<I2, DK2, OK2, any>): this;
/**
* Returns either `objectLookup` or `primitiveLookup` based on given `field` and `value`
*
* @ignore
* @param key is array item's field to get lookup for.
* @param value is value stored in that field. (Used for selecting primitve or object lookup)
* @returns map with keys are lookup-values, values are indexes of those values.
*/
private getLookup;
/**
* Adds index into lookup for given field. Also creates lookup if it does not exists.
*
* @ignore
* @param position is position of item which holds the value for the given field.
* @param item is item to add to index.
* @param keys are fields to add lookup. (i.e. "name")
*/
private addToIndex;
/**
* Removes index from lookup for given field. Also deletes lookup if no index remains for this value.
*
* @ignore
* @param item is item to add to index.
* @param position is position of item which holds the value for the given field.
* @param keys are fields to add lookup. (i.e. "name")
*/
private removeFromIndex;
/**
* The `handler.set()` method is a trap for setting a property value of array item.
*
* @ignore
* @param property is the name or Symbol of the property to set.
* @param newItem is the new value of the property to set.
* @returns whether that assignment succeeded
*/
private setProperty;
/**
* The `handler.deleteProperty()` method is a trap for setting a property value of array item.
*
* @ignore
* @param property is the name or Symbol of the property to delete.
* @returns whether delete operation succeeded
*/
private deleteProperty;
/**
* Returns positive index from start for given positive or negative index. Negative index is used for indexing from the end of array.
*
* @ignore
* @param index is index to get positive for.
* @returns positive index for given index.
* @example
* const indexedArray = IndexedArray.create({ id: 98 }, { id: 43 }, { id: 34 }, { id: 23 });
* indexedArray.positiveIndexOf(-1); // 3
* indexedArray.positiveIndexOf(1); // 1
*/
private positiveIndexOf;
/**
* Throws error if an index based operation is accessed when index is disabled.
*
* @private
*/
private assertIndexEnabled;
push(...items: I[]): number;
splice(start: number, deleteCount?: number, ...items: I[]): I[];
sort(compareFn?: (a: I, b: I) => number): this;
/**
* Sorts the elements of an array by given key in place and returns the sorted array.
*
* @param key is the key to sort array by.
* @returns this instance.
*/
sortBy(key?: DK | OK): this;
filter<S extends I>(callbackfn: (value: I, index: number, array: IndexableArray<I, DK, OK, TH>) => value is S, thisArg?: any): IndexableArray<S, DK, OK, TH>;
filter(callbackfn: Callback<I, DK, OK, TH, unknown>, thisArg?: any): IndexableArray<I, DK, OK, TH>;
map<U extends Pick<I, DK | OK>, DK2 extends keyof U = DK, OK2 extends keyof U = OK>(callbackFn: Callback<I, DK, OK, TH, U>, defaultKey?: DK2, ...indexKeys: OK2[]): IndexableArray<U, DK2, Exclude<OK2, DK2>, TH>;
map<U extends Pick<I, DK | OK>, DK2 extends keyof U = DK, OK2 extends keyof U = OK>(callbackFn: Callback<I, DK, OK, TH, U>, thisArg: Record<string, unknown>, defaultKey?: DK2, ...indexKeys: OK2[]): IndexableArray<U, DK2, Exclude<OK2, DK2>, TH>;
map<U extends Record<string, unknown>, DK2 extends keyof U, OK2 extends keyof U = never>(callbackFn: Callback<I, DK, OK, TH, U>, defaultKey: DK2, ...indexKeys: OK2[]): IndexableArray<U, DK2, Exclude<OK2, DK2>, TH>;
map<U extends Record<string, unknown>, DK2 extends keyof U, OK2 extends keyof U = never>(callbackFn: Callback<I, DK, OK, TH, U>, thisArg: Record<string, unknown>, defaultKey: DK2, ...indexKeys: OK2[]): IndexableArray<U, DK2, Exclude<OK2, DK2>, TH>;
map<U extends Record<string, unknown>>(callbackFn: Callback<I, DK, OK, TH, U>, thisArg?: Record<string, unknown>): IndexableArray<U, AvailableDefaultIndex<U, DK, OK>, AvailableIndex<U, DK, OK>, TH>;
map<U extends any>(callbackFn: Callback<I, DK, OK, TH, U>, thisArg?: Record<string, unknown>): IndexableArray<U, AvailableDefaultIndex<U, DK, OK>, AvailableIndex<U, DK, OK>, TH>;
flatMap<U extends Pick<I, DK | OK>, DK2 extends keyof U = DK, OK2 extends keyof U = OK, This extends undefined | Record<string, unknown> = undefined>(callbackFn: CallbackThis<I, DK, OK, TH, U | readonly U[], This>, defaultKey?: DK2, ...indexKeys: OK2[]): IndexableArray<U, DK2, Exclude<OK2, DK2>, TH>;
flatMap<U extends Pick<I, DK | OK>, DK2 extends keyof U = DK, OK2 extends keyof U = OK, This extends undefined | Record<string, unknown> = undefined>(callbackFn: CallbackThis<I, DK, OK, TH, U | readonly U[], This>, thisArg: This, defaultKey?: DK2, ...indexKeys: OK2[]): IndexableArray<U, DK2, Exclude<OK2, DK2>, TH>;
flatMap<U extends Record<string, unknown>, DK2 extends keyof U, OK2 extends keyof U = never, This extends undefined | Record<string, unknown> = undefined>(callbackFn: CallbackThis<I, DK, OK, TH, U | readonly U[], This>, defaultKey: DK2, ...indexKeys: OK2[]): IndexableArray<U, DK2, Exclude<OK2, DK2>, TH>;
flatMap<U extends Record<string, unknown>, DK2 extends keyof U, OK2 extends keyof U = never, This extends undefined | Record<string, unknown> = undefined>(callbackFn: CallbackThis<I, DK, OK, TH, U | readonly U[], This>, thisArg: This, defaultKey: DK2, ...indexKeys: OK2[]): IndexableArray<U, DK2, Exclude<OK2, DK2>, TH>;
flatMap<U extends Record<string, unknown>, This extends undefined | Record<string, unknown> = undefined>(callbackFn: CallbackThis<I, DK, OK, TH, U | readonly U[], This>, thisArg?: This, ...rest: never[]): IndexableArray<U, AvailableDefaultIndex<U, DK, OK>, AvailableIndex<U, DK, OK>, TH>;
flatMap<U extends any, This extends undefined | Record<string, unknown> = undefined>(callbackFn: CallbackThis<I, DK, OK, TH, U | readonly U[], This>, thisArg?: This, ...rest: never[]): IndexableArray<U, AvailableDefaultIndex<U, DK, OK>, AvailableIndex<U, DK, OK>, TH>;
/**
* Creates a new base Array (not IndexableArray) with the results of calling a provided function on every element in the calling array.
*
* @param callbackfn is function that produces an element of the new Array, taking three arguments: `value`, `index` and `indexableArray`.
* @param thisArg is value to use as this when executing callback.
* @returns a new `Array` with each element being the result of the callback function.
* @see {@link IndexableArray#map} to get an `IndexableArray`.
* @example
* const usersWithName = new IndexableArray({ id: 23, name: "Geroge" }, { id: 96, name: "Lisa" }).addIndex("name");
* const baseArray = usersWithName.mapToArray(user => ({ id: user.id, nick: name.substring(0,2) })); // Normal base array.
*/
mapToArray<U>(callbackfn: Callback<I, DK, OK, TH, U | readonly U[]>, thisArg?: any): U[];
slice(start?: number, end?: number): IndexableArray<I, DK, OK, TH>;
concat(...items: ConcatArray<I>[]): IndexableArray<I, DK, OK, TH>;
concat(...items: (I | ConcatArray<I>)[]): IndexableArray<I, DK, OK, TH>;
/**
* Sets default index key to be used with lookup functions. Returns same instance.
*
* @param key is key to be used as default index with lookup functions.
* @returns this object.
* @example
* const input = [{ id: 23, name: "Geroge" }, { id: 96, name: "Lisa" }];
* let users = new IndexableArray(...input).addIndex("name", "id"); // "name" is default index
* users = users.withDefaultIndex("id"); // "id" is default index. Assignment is used for TypeScript to assign right type to variable.
*/
withDefaultIndex<K extends OK>(key: K): IndexableArray<I, K, OK, TH>;
/**
* Returns the first index at which a given indexed value can be found in the array, or -1 if it is not present.
*
* @param value indexed value to search for.
* @param options are option to modify behaviour.
* @param options.key is index field to look for value. Default lookup field is used if no key is provided.
* @param options.fromIndex is the index to start the search at. If the index is greater than or equal to the array's length, -1 is returned, which means the array will not be searched. If the provided index value is a negative number, it is taken as the offset from the end of the array
* @returns the first index of the element in the array; -1 if not found.
*/
getIndex<K extends DK | OK>(value: I[K], { key, fromIndex }?: {
key?: K;
fromIndex?: number;
}): number;
/**
* Returns all indexes at which a given indexed value can be found in the array, or empty array if it is not present.
*
* @param value indexed value to search for.
* @param options are option to modify behaviour.
* @param options.key is index field to look for value. Default lookup field is used if no key is provided.
* @returns all indexes of the element in the array; Empty array if not found.
*/
getAllIndexes<K extends OK | DK>(value: I[K], { key }?: {
key?: K;
}): number[];
/**
* Returns the first item at which a given indexed value can be found in the array. According to construction option or `throwUnknown` option,
* returns `undefined` or throws exception if value cannot be found.
*
* @param value is indexed value to search for.
* @param options are options to modify behaviour.
* @param options.key is index field to look for value. Default lookup field is used if no key is provided.
* @param options.fromIndex is the index to start the search at. If the index is greater than or equal to the array's length, -1 is returned, which means the array will not be searched. If the provided index value is a negative number, it is taken as the offset from the end of the array
* @param options.throwUnknown is whether to throw exception if value cannot be found in index.
* @returns the first item with given indexed value in the array; `undefined` if not found.
*/
get<K extends DK | OK, TH2 extends boolean | undefined = TH>(value: I[K], { key, fromIndex, throwUnknown, }?: {
key?: K;
fromIndex?: number;
throwUnknown?: TH2;
}): TH2 extends true ? I : I | undefined;
/**
* Returns the first item at which a given indexed value can be found in the array, or throws exception if it is not present.
*
* @param value is indexed value to search for.
* @param options are options to modify behaviour.
* @param options.key is index field to look for value. Default lookup field is used if no key is provided.
* @param options.fromIndex is the index to start the search at. If the index is greater than or equal to the array's length, -1 is returned, which means the array will not be searched. If the provided index value is a negative number, it is taken as the offset from the end of the array
* @returns the first item with given indexed value in the array; `undefined` if not found.
*/
getSure<K extends DK | OK>(value: I[K], { key, fromIndex }?: {
key?: K;
fromIndex?: number;
}): I;
/**
* Returns the first item at which a given indexed value can be found in the array. Returns `undefined` if value cannot be found.
*
* @param value is indexed value to search for.
* @param options are options to modify behaviour.
* @param options.key is index field to look for value. Default lookup field is used if no key is provided.
* @param options.fromIndex is the index to start the search at. If the index is greater than or equal to the array's length, -1 is returned, which means the array will not be searched. If the provided index value is a negative number, it is taken as the offset from the end of the array
* @returns is the first item with given indexed value in the array; `undefined` if not found.
*/
getMaybe<K extends DK | OK>(value: I[K], { key, fromIndex }?: {
key?: K;
fromIndex?: number;
}): I | undefined;
/**
* Returns all items at which a given indexed value can be found in the array, or empty array if it is not present.
*
* @param value is indexed value to search for.
* @param options are options to modify behaviour.
* @param options.key is index field to look for value. Default lookup field is used if no key is provided.
* @returns all items with given indexed value in the array; Empty array if not found.
*/
getAll<K extends DK | OK>(value: I[K], { key }?: {
key?: K;
}): I[];
/**
* Determines whether an array includes a certain indexed value among its entries' keys, returning true or false as appropriate.
*
* @param value is indexed value to search for.
* @param options are options to modify behaviour.
* @param options.key is index field to look for value. Default lookup field is used if no key is provided.
* @param options.fromIndex is the index to start the search at. If the index is greater than or equal to the array's length, -1 is returned, which means the array will not be searched. If the provided index value is a negative number, it is taken as the offset from the end of the array
* @returns true if indexed value is found among array's entries' keys.
*/
has<K extends DK | OK>(value: I[K], { key, fromIndex }?: {
key?: K;
fromIndex?: number;
}): boolean;
/**
* Sets value at path of the object, which is one of the entires of array. To update fields of the objects, this method should be used. Otherwise
* index cannot be updated, because sub fileds are not tracked for chage detection.
*
* @param position is index of the item to be changed.
* @param path is item's path where value to be changed at.
* @param value is new value to be assigned.
* @example
* indexedArray[0].name = "DON'T DO THIS"; // WRONG: Sub fields (i.e. [0]."name") of the array is not watched, so index does not get updated.
* indexedArray.set(0, "name", "OK"); // Index updated.
*
*/
set(position: number, path: string, value: any): void;
/**
* Disables indexing of the array. It may be used to disable temporarily
* - to do heavy updates for performance reasons,
* - to do operations in sub fields.
* If indexing is not needed anymore, it is suggested to create a new native non-extended array and copy values into it
* for avoiding performance penalty of proxy array used in this library.
*
* @see {IndexedArray#enableIndex} method.
* @example
* indexedArray.disableIndex();
* indexedArray[0].name = "THIS IS OK NOW";
* indexedArray.enableIndex(); // Index is recreated from scratch.
*/
disableIndex(): void;
/**
* Enables indexing and recreates index from scratch.
*
* @see {IndexedArray#disableIndex} method.
*/
enableIndex(): void;
}
export {};
//# sourceMappingURL=index.d.ts.map