I have implemented Aggregate method in my Linq library written for Typescript.
if (!Array.prototype.Aggregate) {
Array.prototype.Aggregate = function <T>(accumulator: any, currentValue: any, reducerFunc: (accumulator: any, currentValue: any) => any): any {
//debugger
if (reducerFunc === undefined || reducerFunc === null) {
reducerFunc = (accumulator, currentValue) => accumulator + currentValue;
}
let result = this.reduce(reducerFunc);
return result;
}
}
if (!Array.prototype.AggregateSelect) {
Array.prototype.AggregateSelect = function <T>(property: (keyof T), accumulator: any, currentValue: any, reducerFunc: (accumulator: any, currentValue: any) => any): any {
//debugger
if (reducerFunc === undefined || reducerFunc === null) {
reducerFunc = (accumulator, currentValue) => accumulator + currentValue;
}
//debugger
let result = this.Select(property).map(n => n[property]).reduce(reducerFunc);
return result;
}
}
Here are some Jasmine tests for these two methods - note that I also support setting the initial value.
it('can aggregate items to expected result using Aggregate on array of items of numbers', () => {
let someNums = [1, 2, 3, 4];
let result = someNums.Aggregate(0, 0, null);
expect(result).toBe(10);
});
it('can aggregate items and project to expected result using AggregateSelect on array of items of objects', () => {
let someArray: any[] = [];
someArray.push(<SomeClass>{ Name: "Foo", Num: 1 });
someArray.push(<SomeClass>{ Name: "FooBaz", Num: 4 });
someArray.push(<SomeClass>{ Name: "AllyoBaze", Num: 7 });
let result = someArray.AggregateSelect<SomeClass>("Num", 0, 0, null);
expect(result).toBe(12);
});
it('can aggregate once more items and project to expected result using AggregateSelect on array of items of objects with accumulator value set initially', () => {
let someArray: Student[] = [];
someArray.push(<Student>{ StudentID: 1, StudentName: "John", Age: 13 });
someArray.push(<Student>{ StudentID: 2, StudentName: "Moin", Age: 21 });
someArray.push(<Student>{ StudentID: 3, StudentName: "Bill", Age: 18 });
someArray.push(<Student>{ StudentID: 4, StudentName: "Ram", Age: 20 });
someArray.push(<Student>{ StudentID: 5, StudentName: "Ron", Age: 15 });
let result = someArray.AggregateSelect<Student>("StudentName", "Student Names: ", 0, (a, b) => a + "," + b);
expect(result).toBe("John,Moin,Bill,Ram,Ron");
});
My interface definition is growing everytime for my Linq Library! Here is how it looks now:
export { } //creating a module of below code
declare global {
type predicate<T> = (arg: T) => boolean;
type sortingValue<T> = (arg: T) => any;
interface Array<T> {
FirstOrDefault<T>(condition: predicate<T>): T;
LastOrDefault<T>(condition: predicate<T>): T;
Where<T>(condition: predicate<T>): T[];
Count<T>(): number;
CountBy<T>(condition: predicate<T>): number;
Select<T>(...properties: (keyof T)[]): any[];
GroupBy<T>(groupFunc: (arg: T) => string): any[];
EnumerableRange(start: number, count: number): number[];
Any<T>(condition: predicate<T>): boolean;
All<T>(condition: predicate<T>): boolean;
MaxSelect<T>(property: (keyof T)): any;
MinSelect<T>(property: (keyof T)): any;
Average<T>(): number;
AverageSelect<T>(property: (keyof T)): number;
Max(): any;
Min(): any;
Sum(): any;
Distinct<T>(): T[];
DistinctBy<T>(property: (keyof T)): any;
SumSelect<T>(property: (keyof T)): any;
Intersect<T>(otherArray: T[]): T[];
IntersectSelect<T>(property: (keyof T), otherArray: T[]): T[];
MinSelect<T>(property: (keyof T)): any;
OrderBy<T>(sortMember: sortingValue<T>): T[];
OrderByDescending<T>(sortMember: sortingValue<T>): T[];
ThenBy<T>(sortMember: sortingValue<T>): T[];
OfType<T>(compareObject: T): T[];
SequenceEqual<T>(compareArray: T): boolean;
Take<T>(count: number): T[];
TakeWhile<T>(condition: predicate<T>): T[];
SkipWhile<T>(condition: predicate<T>): T[];
Skip<T>(count: number): T[];
defaultComparerSort<T>(x: T, y: T);
ElementAt<T>(index: number);
ElementAtOrDefault<T>(index: number);
Aggregate<T>(accumulator: any, currentValue: any, reducerFunc: (accumulator: any, currentValue: any) => any): any;
AggregateSelect<T>(property: (keyof T), accumulator: any, currentValue: any, reducerFunc: (accumulator: any, currentValue: any) => any): any;
}
}