Skip to content

Commit 2d94e2a

Browse files
feat(iterables): Add non-by aggregation functions
Add non-by variants of sum, min, max and mean functions. re: #6
1 parent 59afe8b commit 2d94e2a

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

src/iterables.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,18 @@ export function* reverse<T>(source: Iterable<T>): Iterable<T> {
615615
}
616616
}
617617

618+
/**
619+
* Returns the sum of the values in the collection.
620+
* @param source The input collection.
621+
*/
622+
export function sum(source: Iterable<number>): number {
623+
let sum = 0
624+
for (const item of source) {
625+
sum += item
626+
}
627+
return sum
628+
}
629+
618630
/**
619631
* Returns the sum of the values returned by the selector for each element in the collection.
620632
* @param selector A function to transform each element into a summable value.
@@ -640,6 +652,24 @@ export function sumBy<T>(a: any, b?: any): any {
640652
return partial ? exec : exec(a)
641653
}
642654

655+
/**
656+
* Returns the maximum of the values in the collection.
657+
* @param source The input collection.
658+
* @throws If the collection is empty.
659+
*/
660+
export function max(source: Iterable<number>): number {
661+
let max: number | null = null
662+
for (const item of source) {
663+
if (max === null || item > max) {
664+
max = item
665+
}
666+
}
667+
if (max === null) {
668+
throw new Error(`Can't find max of an empty collection`)
669+
}
670+
return max
671+
}
672+
643673
/**
644674
* Returns the maximum of the values returned by the selector for each element in the collection.
645675
* @param selector A function to transform each element into a comparable value.
@@ -673,6 +703,24 @@ export function maxBy<T>(a: any, b?: any): any {
673703
return partial ? exec : exec(a)
674704
}
675705

706+
/**
707+
* Returns the minimum of the values in the collection.
708+
* @param source The input collection.
709+
* @throws If the collection is empty.
710+
*/
711+
export function min(source: Iterable<number>): number {
712+
let min: number | null = null
713+
for (const item of source) {
714+
if (min === null || item < min) {
715+
min = item
716+
}
717+
}
718+
if (min === null) {
719+
throw new Error(`Can't find min of an empty collection`)
720+
}
721+
return min
722+
}
723+
676724
/**
677725
* Returns the minimum of the values returned by the selector for each element in the collection.
678726
* @param selector A function to transform each element into a comparable value.
@@ -706,6 +754,24 @@ export function minBy<T>(a: any, b?: any): any {
706754
return partial ? exec : exec(a)
707755
}
708756

757+
/**
758+
* Returns the mean (average) of the values in the collection.
759+
* @param source The input collection.
760+
* @throws If the collection is empty.
761+
*/
762+
export function mean(source: Iterable<number>): number {
763+
let sum = 0
764+
let count = 0
765+
for (const item of source) {
766+
sum += item
767+
count++
768+
}
769+
if (count === 0) {
770+
throw new Error(`Can't find mean of an empty collection`)
771+
}
772+
return sum / count
773+
}
774+
709775
/**
710776
* Returns the mean (average) of the values returned by the selector for each element in the collection.
711777
* @param selector A function to transform each element into a summable value.

test/iterable.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,20 @@ describe('reverse', () => {
824824
})
825825
})
826826

827+
describe('sum', () => {
828+
it('sums without partial application', () => {
829+
expect(
830+
Iterables.sum(
831+
(function*() {
832+
yield 21
833+
yield 2
834+
yield 18
835+
})()
836+
)
837+
).toEqual(41)
838+
})
839+
})
840+
827841
describe('sumBy', () => {
828842
it('sums ages', () => {
829843
expect(
@@ -850,6 +864,23 @@ describe('sumBy', () => {
850864
})
851865
})
852866

867+
describe('max', () => {
868+
it('finds max', () => {
869+
expect(
870+
Iterables.max(
871+
(function*() {
872+
yield 2
873+
yield 21
874+
yield 18
875+
})()
876+
)
877+
).toEqual(21)
878+
})
879+
it('fails on empty collection', () => {
880+
expect(() => Iterables.max([])).toThrow(`Can't find max of an empty collection`)
881+
})
882+
})
883+
853884
describe('maxBy', () => {
854885
it('finds max age', () => {
855886
expect(
@@ -881,6 +912,23 @@ describe('maxBy', () => {
881912
})
882913
})
883914

915+
describe('min', () => {
916+
it('finds min', () => {
917+
expect(
918+
Iterables.min(
919+
(function*() {
920+
yield 21
921+
yield 2
922+
yield 18
923+
})()
924+
)
925+
).toEqual(2)
926+
})
927+
it('fails on empty collection', () => {
928+
expect(() => Iterables.min([])).toThrow(`Can't find min of an empty collection`)
929+
})
930+
})
931+
884932
describe('minBy', () => {
885933
it('finds min age', () => {
886934
expect(
@@ -912,6 +960,24 @@ describe('minBy', () => {
912960
})
913961
})
914962

963+
describe('mean', () => {
964+
it('finds mean', () => {
965+
expect(
966+
Iterables.mean(
967+
(function*() {
968+
yield 21
969+
yield 2
970+
yield 18
971+
yield 39
972+
})()
973+
)
974+
).toEqual(20)
975+
})
976+
it('fails on empty collection', () => {
977+
expect(() => Iterables.mean([])).toThrow(`Can't find mean of an empty collection`)
978+
})
979+
})
980+
915981
describe('meanBy', () => {
916982
it('finds mean age', () => {
917983
expect(

0 commit comments

Comments
 (0)