Skip to content

Commit 1fc1ace

Browse files
feat: add geometric mean algorithm
1 parent 5c39e87 commit 1fc1ace

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

‎Maths/GeometricMean.js‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @function geometricMean
3+
* @description Calculates the geometric mean of an array of positive numbers.
4+
* The geometric mean is the nth root of the product of n numbers.
5+
* Formula: (x1 * x2 * ... * xn)^(1/n)
6+
* @param {number[]} arr - An array of positive numbers
7+
* @returns {number} - The geometric mean
8+
* @see https://en.wikipedia.org/wiki/Geometric_mean
9+
* @example geometricMean([1, 2, 3, 4]) => 2.213
10+
* @example geometricMean([2, 8]) => 4
11+
* @example geometricMean([4, 4, 4]) => 4
12+
*/
13+
const geometricMean = (arr) => {
14+
if (!Array.isArray(arr) || arr.length === 0) {
15+
throw new TypeError('Input must be a non-empty array')
16+
}
17+
if (!arr.every((n) => typeof n === 'number' && !isNaN(n) && n > 0)) {
18+
throw new TypeError('All elements must be positive numbers')
19+
}
20+
const product = arr.reduce((acc, n) => acc * n, 1)
21+
return Math.pow(product, 1 / arr.length)
22+
}
23+
24+
export { geometricMean }

‎Maths/test/GeometricMean.test.js‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { geometricMean } from '../GeometricMean.js'
2+
3+
describe('geometricMean', () => {
4+
test('geometricMean([2, 8]) returns 4', () => {
5+
expect(geometricMean([2, 8])).toBeCloseTo(4)
6+
})
7+
8+
test('geometricMean([4, 4, 4]) returns 4', () => {
9+
expect(geometricMean([4, 4, 4])).toBeCloseTo(4)
10+
})
11+
12+
test('geometricMean([1, 2, 3, 4]) returns ~2.213', () => {
13+
expect(geometricMean([1, 2, 3, 4])).toBeCloseTo(2.213, 2)
14+
})
15+
16+
test('geometricMean single element returns that element', () => {
17+
expect(geometricMean([5])).toBeCloseTo(5)
18+
})
19+
20+
test('geometricMean([1, 1, 1]) returns 1', () => {
21+
expect(geometricMean([1, 1, 1])).toBeCloseTo(1)
22+
})
23+
24+
test('geometricMean([2, 2]) returns 2', () => {
25+
expect(geometricMean([2, 2])).toBeCloseTo(2)
26+
})
27+
28+
test('geometricMean([3, 27]) returns 9', () => {
29+
expect(geometricMean([3, 27])).toBeCloseTo(9)
30+
})
31+
32+
test('geometricMean does not mutate original array', () => {
33+
const arr = [1, 2, 3, 4]
34+
geometricMean(arr)
35+
expect(arr).toEqual([1, 2, 3, 4])
36+
})
37+
38+
test('throws TypeError for empty array', () => {
39+
expect(() => geometricMean([])).toThrow(TypeError)
40+
})
41+
42+
test('throws TypeError for non-array input', () => {
43+
expect(() => geometricMean('123')).toThrow(TypeError)
44+
})
45+
46+
test('throws TypeError for zero values', () => {
47+
expect(() => geometricMean([1, 0, 3])).toThrow(TypeError)
48+
})
49+
50+
test('throws TypeError for negative values', () => {
51+
expect(() => geometricMean([1, -2, 3])).toThrow(TypeError)
52+
})
53+
})

0 commit comments

Comments
 (0)