|
|
|
@ -11,9 +11,21 @@ type AsyncCollectionComparable<T> = CollectionItem<T>[] | Collection<T> | AsyncC
|
|
|
|
|
type AsyncKeyFunction<T, T2> = (item: CollectionItem<T>, index: number) => CollectionItem<T2> | Promise<CollectionItem<T2>>
|
|
|
|
|
type AsyncCollectionFunction<T, T2> = (items: AsyncCollection<T>) => T2
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Like a collection, but asynchronous.
|
|
|
|
|
*/
|
|
|
|
|
export class AsyncCollection<T> {
|
|
|
|
|
constructor(
|
|
|
|
|
/**
|
|
|
|
|
* Iterable of items to base this collction on.
|
|
|
|
|
* @type Iterable
|
|
|
|
|
*/
|
|
|
|
|
private _items: Iterable<T>,
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Size to use when chunking results for memory-optimization.
|
|
|
|
|
* @type number
|
|
|
|
|
*/
|
|
|
|
|
private _chunk_size: number = 1000, // TODO fix this. It's just for testing
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
@ -57,14 +69,27 @@ export class AsyncCollection<T> {
|
|
|
|
|
await this._items.reset()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get all items in this collection as an array.
|
|
|
|
|
* @return Promise<array>
|
|
|
|
|
*/
|
|
|
|
|
async all(): Promise<CollectionItem<T>[]> {
|
|
|
|
|
return (await this._items.from_range(0, await this._items.count())).all()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get all items in this collection as a synchronous Collection
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async collect(): Promise<Collection<T>> {
|
|
|
|
|
return this._items.from_range(0, await this._items.count())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the average value of the collection or one of its keys.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<number>
|
|
|
|
|
*/
|
|
|
|
|
async average<T2>(key?: KeyOperator<T, T2>): Promise<number> {
|
|
|
|
|
let running_total = 0
|
|
|
|
|
let running_items = 0
|
|
|
|
@ -82,6 +107,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return running_total / running_items
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the median value of the collection or one of its keys.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<number>
|
|
|
|
|
*/
|
|
|
|
|
async median<T2>(key?: KeyOperator<T, T2>): Promise<number> {
|
|
|
|
|
let items: number[] = []
|
|
|
|
|
|
|
|
|
@ -100,6 +130,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
else return (items[middle] + items[middle + 1]) / 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the mode value of the collection or one of its keys.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<number>
|
|
|
|
|
*/
|
|
|
|
|
async mode<T2>(key?: KeyOperator<T, T2>): Promise<number> {
|
|
|
|
|
let counts: any = {}
|
|
|
|
|
|
|
|
|
@ -118,11 +153,24 @@ export class AsyncCollection<T> {
|
|
|
|
|
return Number(Object.keys(counts).reduce((a, b) => counts[a] > counts[b] ? a : b)[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* If this collection contains nested collections, collapse them to a single level.
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async collapse(): Promise<Collection<any>> {
|
|
|
|
|
const items = await this.collect()
|
|
|
|
|
return items.collapse() as Collection<any>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if the collection contains an item satisfying the given collection.
|
|
|
|
|
* @example
|
|
|
|
|
* collection.contains('id', '>', 4)
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {WhereOperator} operator
|
|
|
|
|
* @param [operand]
|
|
|
|
|
* @return Promise<boolean>
|
|
|
|
|
*/
|
|
|
|
|
async contains<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): Promise<boolean> {
|
|
|
|
|
let contains = false
|
|
|
|
|
|
|
|
|
@ -137,10 +185,19 @@ export class AsyncCollection<T> {
|
|
|
|
|
return contains
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a clean instance of this collection pointing to the same result set of the iterable.
|
|
|
|
|
* @return Promise<AsyncCollection>
|
|
|
|
|
*/
|
|
|
|
|
async clone(): Promise<AsyncCollection<T>> {
|
|
|
|
|
return new AsyncCollection<T>(await this._items.clone())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the elements that are different between the two collections.
|
|
|
|
|
* @param {AsyncCollectionComparable} items
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async diff(items: AsyncCollectionComparable<T>): Promise<Collection<T>> {
|
|
|
|
|
const matches: T[] = []
|
|
|
|
|
|
|
|
|
@ -155,6 +212,13 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(matches)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the elements that are different between the two collections, using the given function
|
|
|
|
|
* as a comparator for the elements.
|
|
|
|
|
* @param {AsyncCollectionComparable} items
|
|
|
|
|
* @param {DeterminesEquality} compare
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async diffUsing(items: AsyncCollectionComparable<T>, compare: DeterminesEquality<T>): Promise<Collection<T>> {
|
|
|
|
|
const matches: T[] = []
|
|
|
|
|
|
|
|
|
@ -168,6 +232,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(matches)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if the given item is present in the collection.
|
|
|
|
|
* @param item
|
|
|
|
|
* @return Promise<boolean>
|
|
|
|
|
*/
|
|
|
|
|
async includes(item: CollectionItem<T>): Promise<boolean> {
|
|
|
|
|
let contains = false
|
|
|
|
|
|
|
|
|
@ -181,6 +250,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return contains
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if there is an item in the collection for which the given operator returns true.
|
|
|
|
|
* @param {function} operator - item => boolean
|
|
|
|
|
* @return Promise<boolean>
|
|
|
|
|
*/
|
|
|
|
|
async some(operator: (item: T) => boolean): Promise<boolean> {
|
|
|
|
|
let contains = false
|
|
|
|
|
|
|
|
|
@ -195,7 +269,12 @@ export class AsyncCollection<T> {
|
|
|
|
|
|
|
|
|
|
return contains
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies a callback to each item in the collection.
|
|
|
|
|
* @param {AsyncKeyFunction} func
|
|
|
|
|
* @return Promise<void>
|
|
|
|
|
*/
|
|
|
|
|
async each<T2>(func: AsyncKeyFunction<T, T2>): Promise<void> {
|
|
|
|
|
let index = 0
|
|
|
|
|
|
|
|
|
@ -207,6 +286,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies a callback to each item in the collection and returns the results as a collection.
|
|
|
|
|
* @param {AsyncKeyFunction} func
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async map<T2>(func: AsyncKeyFunction<T, T2>): Promise<Collection<T2>> {
|
|
|
|
|
const new_items: CollectionItem<T2>[] = []
|
|
|
|
|
await this.each(async (item, index) => {
|
|
|
|
@ -215,6 +299,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T2>(new_items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if the given operator returns true for every item in the collection.
|
|
|
|
|
* @param {AsyncKeyFunction} func
|
|
|
|
|
* @return Promise<boolean>
|
|
|
|
|
*/
|
|
|
|
|
async every<T2>(func: AsyncKeyFunction<T, T2>): Promise<boolean> {
|
|
|
|
|
let pass = true
|
|
|
|
|
let index = 0
|
|
|
|
@ -233,6 +322,12 @@ export class AsyncCollection<T> {
|
|
|
|
|
return pass
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if every item in the collection satisfies the given where clause.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {WhereOperator} operator
|
|
|
|
|
* @param [operand]
|
|
|
|
|
*/
|
|
|
|
|
async everyWhere<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): Promise<boolean> {
|
|
|
|
|
let pass = true
|
|
|
|
|
|
|
|
|
@ -246,6 +341,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return pass
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies a filter to every item in the collection and returns the results that pass the filter.
|
|
|
|
|
* @param {KeyFunction} func
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async filter<T2>(func: KeyFunction<T, T2>): Promise<Collection<T>> {
|
|
|
|
|
let new_items: CollectionItem<T>[] = []
|
|
|
|
|
|
|
|
|
@ -256,16 +356,35 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(new_items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calls the passed in function if the boolean condition is true. Allows for functional syntax.
|
|
|
|
|
* @param {boolean} bool
|
|
|
|
|
* @param {AsyncCollectionFunction} then
|
|
|
|
|
* @return AsyncCollection
|
|
|
|
|
*/
|
|
|
|
|
when<T2>(bool: boolean, then: AsyncCollectionFunction<T, T2>): AsyncCollection<T> {
|
|
|
|
|
if ( bool ) then(this)
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calls the passed in function if the boolean condition is false. Allows for functional syntax.
|
|
|
|
|
* @param {boolean} bool
|
|
|
|
|
* @param {AsyncCollectionFunction} then
|
|
|
|
|
* @return AsyncCollection
|
|
|
|
|
*/
|
|
|
|
|
unless<T2>(bool: boolean, then: AsyncCollectionFunction<T, T2>): AsyncCollection<T> {
|
|
|
|
|
if ( !bool ) then(this)
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies the given where condition to the collection and returns a new collection of the results.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {WhereOperator} operator
|
|
|
|
|
* @param [operand]
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async where<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): Promise<Collection<T>> {
|
|
|
|
|
let new_items: CollectionItem<T>[] = []
|
|
|
|
|
await this._chunk(async items => {
|
|
|
|
@ -274,6 +393,14 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(new_items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies the given where condition to the collection and returns a new collection of the items
|
|
|
|
|
* that did not satisfy the condition.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {WhereOperator} operator
|
|
|
|
|
* @param [operand]
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async whereNot<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): Promise<Collection<T>> {
|
|
|
|
|
let new_items: CollectionItem<T>[] = []
|
|
|
|
|
await this._chunk(async items => {
|
|
|
|
@ -282,6 +409,12 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(new_items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies a WHERE ... IN ... condition to the collection an returns a new collection of the results.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {AsyncCollectionComparable} items
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async whereIn<T2>(key: KeyOperator<T, T2>, items: AsyncCollectionComparable<T2>): Promise<Collection<T>> {
|
|
|
|
|
let new_items: CollectionItem<T>[] = []
|
|
|
|
|
await this._chunk_all_associate(key,async chunk => {
|
|
|
|
@ -294,6 +427,13 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(new_items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies a WHERE ... IN ... condition to the collection and returns a new collection of the items
|
|
|
|
|
* that did not satisfy the condition.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {AsyncCollectionComparable} items
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async whereNotIn<T2>(key: KeyOperator<T, T2>, items: AsyncCollectionComparable<T2>): Promise<Collection<T>> {
|
|
|
|
|
let new_items: CollectionItem<T>[] = []
|
|
|
|
|
await this._chunk_all_associate(key,async chunk => {
|
|
|
|
@ -306,12 +446,23 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(new_items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the first item in the collection, if one exists.
|
|
|
|
|
* @return Promise<MaybeCollectionItem>
|
|
|
|
|
*/
|
|
|
|
|
async first(): Promise<MaybeCollectionItem<T>> {
|
|
|
|
|
if ( await this._items.count() > 0 ) {
|
|
|
|
|
return this._items.at_index(0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the first item in the collection that satisfies the given where condition, if one exists.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {WhereOperator} [operator = '=']
|
|
|
|
|
* @param [operand = true]
|
|
|
|
|
* @return Promise<MaybeCollectionItem>
|
|
|
|
|
*/
|
|
|
|
|
async firstWhere<T2>(key: KeyOperator<T, T2>, operator: WhereOperator = '=', operand: any = true): Promise<MaybeCollectionItem<T>> {
|
|
|
|
|
let item = undefined
|
|
|
|
|
await this._chunk_all_associate(key, async items => {
|
|
|
|
@ -324,6 +475,12 @@ export class AsyncCollection<T> {
|
|
|
|
|
return item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the first item in the collection that does not satisfy the given where condition, if one exists.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {WhereOperator} [operator = '=']
|
|
|
|
|
* @param [operand = true]
|
|
|
|
|
*/
|
|
|
|
|
async firstWhereNot<T2>(key: KeyOperator<T, T2>, operator: WhereOperator = '=', operand: any = true): Promise<MaybeCollectionItem<T>> {
|
|
|
|
|
let item: MaybeCollectionItem<T> = undefined
|
|
|
|
|
await this._chunk(async items => {
|
|
|
|
@ -336,31 +493,67 @@ export class AsyncCollection<T> {
|
|
|
|
|
return item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the number of elements in this collection.
|
|
|
|
|
* @return Promise<number>
|
|
|
|
|
*/
|
|
|
|
|
async count() {
|
|
|
|
|
return this._items.count()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the number of elements in this collection.
|
|
|
|
|
* @return Promise<number>
|
|
|
|
|
*/
|
|
|
|
|
async length() {
|
|
|
|
|
return this._items.count()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the item at the given index of this collection, if one exists.
|
|
|
|
|
* If none exists and a fallback value is provided, that value will be returned.
|
|
|
|
|
* @param {number} index
|
|
|
|
|
* @param [fallback]
|
|
|
|
|
* @return Promise<any>
|
|
|
|
|
*/
|
|
|
|
|
async get(index: number, fallback?: any) {
|
|
|
|
|
if ( (await this.count()) > index ) return this._items.at_index(index)
|
|
|
|
|
else return fallback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the item at the given index of this collection, if one exists.
|
|
|
|
|
* @param {number} index
|
|
|
|
|
*/
|
|
|
|
|
async at(index: number): Promise<MaybeCollectionItem<T>> {
|
|
|
|
|
return this.get(index)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return an object which maps key values to arrays of items in the collection that satisfy that value.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<object>
|
|
|
|
|
*/
|
|
|
|
|
async groupBy<T2>(key: KeyOperator<T, T2>): Promise<any> {
|
|
|
|
|
return (await this.collect()).groupBy(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return an object mapping the given key value to items in this collection.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<object>
|
|
|
|
|
*/
|
|
|
|
|
async associate<T2>(key: KeyOperator<T, T2>): Promise<any> {
|
|
|
|
|
return (await this.collect()).associate(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Join the items in this collection with the given delimiter.
|
|
|
|
|
* @example
|
|
|
|
|
* await collection.join(',') // => '1,2,3,4'
|
|
|
|
|
* @param {string} delimiter
|
|
|
|
|
* @return Promise<string>
|
|
|
|
|
*/
|
|
|
|
|
async join(delimiter: string): Promise<string> {
|
|
|
|
|
let running_strings: string[] = []
|
|
|
|
|
|
|
|
|
@ -371,33 +564,74 @@ export class AsyncCollection<T> {
|
|
|
|
|
return running_strings.join(delimiter)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Join the items in this collection with the given delimiter.
|
|
|
|
|
* @example
|
|
|
|
|
* await collection.implode(',') // => '1,2,3,4'
|
|
|
|
|
* @param {string} delimiter
|
|
|
|
|
* @return Promise<string>
|
|
|
|
|
*/
|
|
|
|
|
async implode(delimiter: string): Promise<string> {
|
|
|
|
|
return this.join(delimiter)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO intersect
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if there are no items in this collection.
|
|
|
|
|
* @return Promise<boolean>
|
|
|
|
|
*/
|
|
|
|
|
async isEmpty(): Promise<boolean> {
|
|
|
|
|
return (await this._items.count()) < 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if there is at least one item in this collection.
|
|
|
|
|
* @return Promise<boolean>
|
|
|
|
|
*/
|
|
|
|
|
async isNotEmpty(): Promise<boolean> {
|
|
|
|
|
return (await this._items.count()) > 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the last item in this collection, if one exists.
|
|
|
|
|
* @return Promise<MaybeCollectionItem>
|
|
|
|
|
*/
|
|
|
|
|
async last(): Promise<MaybeCollectionItem<T>> {
|
|
|
|
|
const length = await this._items.count()
|
|
|
|
|
if ( length > 0 ) return this._items.at_index(length - 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the last item in this collection which satisfies the given where condition, if one exists.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {WhereOperator} operator
|
|
|
|
|
* @param [operand]
|
|
|
|
|
* @return Promise<MaybeCollectionItem>
|
|
|
|
|
*/
|
|
|
|
|
async lastWhere<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): Promise<MaybeCollectionItem<T>> {
|
|
|
|
|
return (await this.where(key, operator, operand)).last()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the last item in this collection which does not satisfy the given condition, if one exists.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @param {WhereOperator} operator
|
|
|
|
|
* @param [operand]
|
|
|
|
|
* @return Promise<MaybeCollectionItem>
|
|
|
|
|
*/
|
|
|
|
|
async lastWhereNot<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): Promise<MaybeCollectionItem<T>> {
|
|
|
|
|
return (await this.whereNot(key, operator, operand)).last()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Builds a collection of the values of a given key for each item in this collection.
|
|
|
|
|
* @example
|
|
|
|
|
* // collection has { a: 1 }, { a: 2 }, { a: 3 }
|
|
|
|
|
* await collection.pluck('a') // => [1, 2, 3]
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async pluck<T2>(key: KeyOperator<T, T2>): Promise<Collection<T2>> {
|
|
|
|
|
let new_items: CollectionItem<T2>[] = []
|
|
|
|
|
|
|
|
|
@ -408,6 +642,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T2>(new_items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the max value of the given key.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<number>
|
|
|
|
|
*/
|
|
|
|
|
async max<T2>(key: KeyOperator<T, T2>): Promise<number> {
|
|
|
|
|
let running_max: number
|
|
|
|
|
|
|
|
|
@ -421,10 +660,20 @@ export class AsyncCollection<T> {
|
|
|
|
|
return running_max
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a collection of items that have the max value of the given key.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async whereMax<T2>(key: KeyOperator<T, T2>): Promise<Collection<T>> {
|
|
|
|
|
return this.where(key, '=', await this.max(key))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the min value of the given key.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<number>
|
|
|
|
|
*/
|
|
|
|
|
async min<T2>(key: KeyOperator<T, T2>): Promise<number> {
|
|
|
|
|
let running_min: number
|
|
|
|
|
|
|
|
|
@ -438,10 +687,20 @@ export class AsyncCollection<T> {
|
|
|
|
|
return running_min
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a collection of items that have the min value of the given key.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async whereMin<T2>(key: KeyOperator<T, T2>): Promise<Collection<T>> {
|
|
|
|
|
return this.where(key, '=', await this.min(key))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Merge the two collections.
|
|
|
|
|
* @param {AsyncCollectionComparable} merge_with
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async merge<T2>(merge_with: AsyncCollectionComparable<T2>): Promise<Collection<T|T2>> {
|
|
|
|
|
let items: T2[]
|
|
|
|
|
if ( merge_with instanceof Collection ) items = await merge_with.all()
|
|
|
|
@ -452,6 +711,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T|T2>([...items, ...await this.all()])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a collection of every nth item in this collection.
|
|
|
|
|
* @param {number} n
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async nth(n: number): Promise<Collection<T>> {
|
|
|
|
|
const matches: CollectionItem<T>[] = []
|
|
|
|
|
let current = 1
|
|
|
|
@ -467,12 +731,21 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(matches)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a collection containing the items that would be on the given page, with the given number of items per page.
|
|
|
|
|
* @param {number} page
|
|
|
|
|
* @param {number} per_page
|
|
|
|
|
*/
|
|
|
|
|
async forPage(page: number, per_page: number): Promise<Collection<T>> {
|
|
|
|
|
const start = page * per_page - per_page
|
|
|
|
|
const end = page * per_page - 1
|
|
|
|
|
return this._items.from_range(start, end)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the value of the function, passing this collection to it.
|
|
|
|
|
* @param {AsyncCollectionFunction} func
|
|
|
|
|
*/
|
|
|
|
|
pipe<T2>(func: AsyncCollectionFunction<T, T2>): any {
|
|
|
|
|
return func(this)
|
|
|
|
|
}
|
|
|
|
@ -484,6 +757,12 @@ export class AsyncCollection<T> {
|
|
|
|
|
}
|
|
|
|
|
}*/ // TODO Fix this
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get n random items from this collection.
|
|
|
|
|
* @todo add safety check for it loop exceeds max number of items
|
|
|
|
|
* @param {number} n
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async random(n: number): Promise<Collection<T>> {
|
|
|
|
|
const random_items: CollectionItem<T>[] = []
|
|
|
|
|
const fetched_indices: number[] = []
|
|
|
|
@ -505,6 +784,12 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(random_items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Collapse the collection into a single value using a reducer function.
|
|
|
|
|
* @param {KeyReducerFunction} reducer
|
|
|
|
|
* @param [initial_value]
|
|
|
|
|
* @return Promise<any>
|
|
|
|
|
*/
|
|
|
|
|
async reduce<T2>(reducer: KeyReducerFunction<T, T2>, initial_value?: T2): Promise<T2 | undefined> {
|
|
|
|
|
let current_value = initial_value
|
|
|
|
|
let index = 0
|
|
|
|
@ -519,6 +804,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return current_value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a collection of items that fail the truth test.
|
|
|
|
|
* @param {AsyncKeyFunction} truth_test
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async reject<T2>(truth_test: AsyncKeyFunction<T, T2>): Promise<Collection<T>> {
|
|
|
|
|
let rejected: CollectionItem<T>[] = []
|
|
|
|
|
|
|
|
|
@ -531,10 +821,19 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T>(rejected)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a reversed collection of this collection's items.
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async reverse(): Promise<Collection<T>> {
|
|
|
|
|
return (await this.collect()).reverse()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Search the collection and return the index of that item, if one exists.
|
|
|
|
|
* @param {CollectionItem} item
|
|
|
|
|
* @return Promise<MaybeCollectionIndex>
|
|
|
|
|
*/
|
|
|
|
|
async search(item: CollectionItem<T>): Promise<MaybeCollectionIndex> {
|
|
|
|
|
let found_index
|
|
|
|
|
let index = 0
|
|
|
|
@ -554,6 +853,10 @@ export class AsyncCollection<T> {
|
|
|
|
|
return found_index
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the next item in the collection and remove it.
|
|
|
|
|
* @return Promise<MaybeCollectionItem>
|
|
|
|
|
*/
|
|
|
|
|
async shift(): Promise<MaybeCollectionItem<T>> {
|
|
|
|
|
const next_item = await this._items.next()
|
|
|
|
|
if ( !next_item.done ) {
|
|
|
|
@ -561,34 +864,75 @@ export class AsyncCollection<T> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Shuffle the items in the collection to a random order.
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async shuffle(): Promise<Collection<T>> {
|
|
|
|
|
return (await this.collect()).shuffle()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a slice of this collection.
|
|
|
|
|
* @param {number} start - the starting index
|
|
|
|
|
* @param {number} end - the ending index
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async slice(start: number, end: number): Promise<Collection<T>> {
|
|
|
|
|
return this._items.from_range(start, end - 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sort the collection, optionally with the given comparison function.
|
|
|
|
|
* @param {ComparisonFunction} compare_func
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async sort(compare_func?: ComparisonFunction<T>): Promise<Collection<T>> {
|
|
|
|
|
return (await this.collect()).sort(compare_func)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sort the collection by the given key.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async sortBy<T2>(key?: KeyOperator<T, T2>): Promise<Collection<T>> {
|
|
|
|
|
return (await this.collect()).sortBy(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Reverse sort the collection, optionally with the given comparison function.
|
|
|
|
|
* @param {ComparisonFunction} compare_func
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async sortDesc(compare_func?: ComparisonFunction<T>): Promise<Collection<T>> {
|
|
|
|
|
return (await this.collect()).sortDesc(compare_func)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Reverse sort the collection by the given key.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async sortByDesc<T2>(key?: KeyOperator<T, T2>): Promise<Collection<T>> {
|
|
|
|
|
return (await this.collect()).sortByDesc(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Splice the collection at the given index. Optionally, removing the given number of items.
|
|
|
|
|
* @param {CollectionIndex} start
|
|
|
|
|
* @param {number} [deleteCount]
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async splice(start: CollectionIndex, deleteCount?: number): Promise<Collection<T>> {
|
|
|
|
|
return (await this.collect()).splice(start, deleteCount)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sum the items in the collection, or the values of the given key.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<number>
|
|
|
|
|
*/
|
|
|
|
|
async sum<T2>(key?: KeyOperator<T, T2>): Promise<number> {
|
|
|
|
|
let running_sum: number = 0
|
|
|
|
|
|
|
|
|
@ -606,6 +950,11 @@ export class AsyncCollection<T> {
|
|
|
|
|
return running_sum
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Take the first n items from the front or back of the collection.
|
|
|
|
|
* @param {number} limit
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async take(limit: number): Promise<Collection<T>> {
|
|
|
|
|
if ( limit === 0 ) return new Collection<T>()
|
|
|
|
|
else if ( limit > 0 ) {
|
|
|
|
@ -616,11 +965,21 @@ export class AsyncCollection<T> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Call the given function, passing in this collection. Allows functional syntax.
|
|
|
|
|
* @param {AsyncCollectionFunction} func
|
|
|
|
|
* @return Promise<AsyncCollection>
|
|
|
|
|
*/
|
|
|
|
|
async tap<T2>(func: AsyncCollectionFunction<T, T2>): Promise<AsyncCollection<T>> {
|
|
|
|
|
await func(this)
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return all the unique values in the collection, or the unique values of the given key.
|
|
|
|
|
* @param {KeyOperator} key
|
|
|
|
|
* @return Promise<Collection>
|
|
|
|
|
*/
|
|
|
|
|
async unique<T2>(key?: KeyOperator<T, T2>): Promise<Collection<T|T2>> {
|
|
|
|
|
const has: CollectionItem<T|T2>[] = []
|
|
|
|
|
|
|
|
|
@ -641,6 +1000,10 @@ export class AsyncCollection<T> {
|
|
|
|
|
return new Collection<T|T2>(has)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cast this collection to an array.
|
|
|
|
|
* @return Promise<array>
|
|
|
|
|
*/
|
|
|
|
|
async toArray(): Promise<any[]> {
|
|
|
|
|
const returns: any = []
|
|
|
|
|
for ( const item of (await this.all()) ) {
|
|
|
|
@ -651,14 +1014,29 @@ export class AsyncCollection<T> {
|
|
|
|
|
return returns
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cast this collection to a JSON string.
|
|
|
|
|
* @param [replacer] - the replacer to use
|
|
|
|
|
* @param {number} [space = 4] number of indentation spaces to use
|
|
|
|
|
*/
|
|
|
|
|
async toJSON(replacer = undefined, space = 4): Promise<string> {
|
|
|
|
|
return JSON.stringify(this.toArray(), replacer, space)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Iterator to support async iteration:
|
|
|
|
|
*
|
|
|
|
|
* @example
|
|
|
|
|
* for await (const item of collection) {}
|
|
|
|
|
*/
|
|
|
|
|
[Symbol.asyncIterator]() {
|
|
|
|
|
return this._items.clone()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a clone of the underlying iterator of this collection.
|
|
|
|
|
* @return Iterable
|
|
|
|
|
*/
|
|
|
|
|
iterator() {
|
|
|
|
|
return this._items.clone()
|
|
|
|
|
}
|
|
|
|
|