In this article i’ll show you how to implement sum
, count
, avg
, min
and max
aggregate functions that applies to all Array instances in typescript.
First we will learn how to extend Array type:
Extend Array prototype
In javascript, you can extend an existing class by adding a new function to its prototype without modifying its source code.
To add new function to Array
type, you basically set the new function to Array.prototype.<functionName>
and you can use the new function for all the instances of the class you create.
In typescript, if you want to extend an existing type with a new function, you must add an interface declaration which contains the new function declaration. Otherwise, when you try to extend the prototype with the new function name, you will get an Property 'functionName' does not exist on type 'any[]'
error.
Here is the example how to add a new function functionName
to Array
.
// If you export nothing, you'll need this. Otherwise, delete it
export {}
declare global {
// existing declarations in the global namespace
interface Array<T> {
// add new function
functionName()
}
}
// set the prototype
Array.prototype.functionName = function() {
// implementation
}
// create new Array instance
const arr = []
// you can call the new added functionName
arr.functionName()
- Since
Array
type is in theglobal
namespace, we defineArray<T>
interface in the global namespace. We put the interface indeclare global { }
- We add the
functionName
function to both interface and prototype.
Sum
Now that we have learned how to extend the Array, let’s implement the sum
function.
I used reduce
function to calculate sum. You can also use forEach
function or for
loop.
export {}
declare global {
interface Array<T> {
sum(): number
}
}
Array.prototype.sum = function<T extends number>(): number {
return (this as Array<T>).reduce((prev, current) => prev + current, 0)
}
const arr = [1, 2, 3, 4]
console.log(arr.sum())
- Since
sum
function only supportsnumber
we make generic type<T extends number>
.
To support arrays with more complex objects you can add map function mapFn: (v: T) => number
. Then you can use sum
function like: arr.sum(x => x.price)
. The avg
, min
and max
implementations will use map function.
export {}
declare global {
interface Array<T> {
sum(mapFn: (v: T) => number): number
}
}
Array.prototype.sum = function<T>(mapFn: (v: T) => number): number {
return (this as Array<T>).map(v => mapFn(v)).reduce((prev, current) => prev + current, 0)
}
interface OrderItem {
price: number
}
const arr: OrderItem[] = [{ price: 50 }, { price: 100 }]
console.log(arr.sum(x => x.price))
You can also combine with filter
function:
arr.filter(x => x.price > 50).sum(x => x.price)
- In this example, array items with a price bigger than 50 are collected.
Count
The count
function simply returns length
of the array.
Array.prototype.count = function<T>(): number {
return (this as Array<T>).length
}
Usage:
arr.count()
Average
The avg
function returns sum / count
of the array.
For arrays with 0 count, it will return 0.
Array.prototype.avg = function<T>(mapFn: (v: T) => number): number {
const arr = this as Array<T>
const count = arr.count()
return count === 0 ? 0 : arr.sum(mapFn) / count
}
Usage:
arr.avg(x => x.price)
Min
The min
function uses Math.min
function.
Singe Math.min
returns Infinity
for empty arrays, min
function returns Infinity
for empty arrays.
Array.prototype.min = function<T>(mapFn: (v: T) => number): number {
return Math.min(...(this as Array<T>).map(v => mapFn(v)))
}
Max
The max
function uses Math.max
function.
Singe Math.max
returns -Infinity
for empty arrays, max
function returns -Infinity
for empty arrays.
Array.prototype.max = function<T>(mapFn: (v: T) => number): number {
return Math.max(...(this as Array<T>).map(v => mapFn(v)))
}
Conclusion
The overall code (aggregates.ts
) looks like this:
export {}
declare global {
interface Array<T> {
sum(mapFn: (v: T) => number): number
avg(mapFn: (v: T) => number): number
min(mapFn: (v: T) => number): number
max(mapFn: (v: T) => number): number
count(): number
}
}
Array.prototype.sum = function<T>(mapFn: (v: T) => number): number {
return (this as Array<T>).map(v => mapFn(v)).reduce((prev: number, current: number): number => prev + current, 0)
}
Array.prototype.count = function<T>(): number {
return (this as Array<T>).length
}
Array.prototype.avg = function<T>(mapFn: (v: T) => number): number {
const arr = this as Array<T>
const count = arr.count()
return count === 0 ? 0 : arr.sum(mapFn) / count
}
Array.prototype.min = function<T>(mapFn: (v: T) => number): number {
return Math.min(...(this as Array<T>).map(v => mapFn(v)))
}
Array.prototype.max = function<T>(mapFn: (v: T) => number): number {
return Math.max(...(this as Array<T>).map(v => mapFn(v)))
}
To use these aggregate functions in your code, you can import the module like this:
import './aggregates'
Thanks for reading.
Happy coding!