UNPKG

indexable-array

Version:

Extended native JavaScript Array which provides indexed lookup similar to native Map.

314 lines 19.5 kB
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