diff --git a/Maths/Variance.js b/Maths/Variance.js new file mode 100644 index 0000000000..734c739255 --- /dev/null +++ b/Maths/Variance.js @@ -0,0 +1,23 @@ +/** + * @function variance + * @description Calculates the variance of an array of numbers. + * Variance measures how far each number in the set is from the mean. + * This calculates population variance. + * @param {number[]} arr - An array of numbers + * @returns {number} - The variance + * @see https://en.wikipedia.org/wiki/Variance + * @example variance([2, 4, 4, 4, 5, 5, 7, 9]) => 4 + * @example variance([1, 2, 3]) => 0.667 + */ +const variance = (arr) => { + if (!Array.isArray(arr) || arr.length === 0) { + throw new TypeError('Input must be a non-empty array') + } + if (!arr.every((n) => typeof n === 'number' && !isNaN(n))) { + throw new TypeError('All elements must be numbers') + } + const mean = arr.reduce((sum, n) => sum + n, 0) / arr.length + return arr.reduce((sum, n) => sum + Math.pow(n - mean, 2), 0) / arr.length +} + +export { variance } diff --git a/Maths/test/Variance.test.js b/Maths/test/Variance.test.js new file mode 100644 index 0000000000..85849f0cc4 --- /dev/null +++ b/Maths/test/Variance.test.js @@ -0,0 +1,45 @@ +import { variance } from '../Variance.js' + +describe('variance', () => { + test('variance([2,4,4,4,5,5,7,9]) returns 4', () => { + expect(variance([2, 4, 4, 4, 5, 5, 7, 9])).toBeCloseTo(4) + }) + + test('variance([1,2,3]) returns ~0.667', () => { + expect(variance([1, 2, 3])).toBeCloseTo(0.667, 2) + }) + + test('variance of identical elements returns 0', () => { + expect(variance([5, 5, 5, 5])).toBe(0) + }) + + test('variance single element returns 0', () => { + expect(variance([42])).toBe(0) + }) + + test('variance works with negative numbers', () => { + expect(variance([-2, -1, 0, 1, 2])).toBeCloseTo(2) + }) + + test('variance([1,2,3,4,5]) returns 2', () => { + expect(variance([1, 2, 3, 4, 5])).toBeCloseTo(2) + }) + + test('variance does not mutate original array', () => { + const arr = [1, 2, 3] + variance(arr) + expect(arr).toEqual([1, 2, 3]) + }) + + test('throws TypeError for empty array', () => { + expect(() => variance([])).toThrow(TypeError) + }) + + test('throws TypeError for non-array input', () => { + expect(() => variance('123')).toThrow(TypeError) + }) + + test('throws TypeError for array with non-numbers', () => { + expect(() => variance([1, 'two', 3])).toThrow(TypeError) + }) +})