Nick Huemmer

16 September 2022

Averages Methods - Extending the Math Object

Averages Methods: Extending the Math Object

In this challenge, we have to both make four different functions to calculate different types of averages, and then add them onto the Math object so that they can be called like other methods on the Math object (like Math.pow, Math.sqrt, Math.max and so on).

It sounds tricky, but really is as simple as using dot notation to add each function to the Math object to make them into methods.

Challlenge

In this exercise the goal is to extend the Math() object adding four methods for calculate different types of averages.

Arithmetic Mean: The division of the sum of the numbers by the quantity of numbers. avg of [A, B, C] ➞ (A + B + C) / 3

Quadratic Mean: Also called Root Mean Square, is the square root of the arithmetic mean of the squared numbers. qAvg of [A, B, C] ➞ ²√ ( (A² + B² + C²) / 3 )

Harmonic Mean: is the reciprocal of the arithmetic mean of the numbers reciprocals. hAvg of [A, B, C] ➞ ( (A⁻¹ + B⁻¹ + C⁻¹) / 3 )⁻¹

Geometric Mean: is the n-th root of the product of the numbers, where n is the quantity of numbers. gAvg of [A, B, C] ➞ ³√ (A * B * C)

For each average type build a function that, given a required parameter (the array containing the numbers) and an optional one (the precision, or number of floating digits to return) returns the result as a number.

Examples

Math.avg([3, 5, 7])➞ 5
//Precision is an optional parameter.
// (3 + 5 + 7) / 3 = 15 / 3 = 5

Math.qAvg([3, 5, 7], 1) ➞ 5.3
// ²√ ( (3² + 5² + 7²) / 3 ) = ²√ (83 / 3) = 5.3

Math.hAvg([3, 5, 7], 1) ➞ 4.4
// Precision is required only for the final result.
// (3⁻¹ + 5⁻¹ + 7⁻¹) / 3 )⁻¹ = (0.676... / 3)⁻¹ = 4.4

Math.gAvg([3, 5, 7], 1) ➞ 4.7
// ³√ (3 * 5 * 7) = ³√ 105 = 4.7

Notes

Pay attention to cumulative rounding errors! Precision is required only if the parameter is indicated and only for the final result. All given arrays are valid ones containing positive numbers greater than zero, either integers or float.

My solution

I made each method as a separate function first, then converted them from arrow function syntax to dot notation so that they could be added to the Math object, e.g. const avg = (arr,p) => to Math.avg = (arr, p).

Adding these methods onto the Math object allows them to be used throughout a program since once they are instantiated, they can be used throughout, in the same way methods like Math.pow or Math.sqrt are used.

Note the optional parameter p that may or may not be included. If it is included, then the toFixed method is called and a + is added to convert the resulting string back into a number.

Math.avg = (arr, p) =>  
   p ? +(arr.reduce((a,c)=> a + c)/arr.length).toFixed(p) : arr.reduce((a,c)=> a + c)/arr.length

Math.qAvg = (arr, p) => 
   p ? +Math.sqrt(arr.map(x=> x**2).reduce((a,c)=> a + c)/arr.length).toFixed(p) : Math.sqrt(arr.map(x=> x**2).reduce((a,c)=> a + c)/arr.length)

Math.hAvg = (arr, p) => 
  p ? +((arr.map(x=> x**-1).reduce((a,c)=> a + c)/arr.length)**-1).toFixed(p) : (arr.map(x=> x**-1).reduce((a,c)=> a + c)/arr.length)**-1

Math.gAvg = (arr, p) => 
  p ? +Math.pow(arr.reduce((a,c)=> a * c), 1/arr.length).toFixed(p)  : Math.pow(arr.reduce((a,c)=> a * c), 1/arr.length)


Math.avg([3,5,7]) // 5
Math.qAvg([3,5,7], 1) // 5.3
Math.hAvg([3, 5, 7], 1) // 4.4
Math.gAvg([3, 5, 7], 1) // 4.7
Math.gAvg([1, 23, 456, 7890]) // 95.37672823128207
Math.avg([0.11, 0.22, 0.33], 1) // 0.2
Math.qAvg([0.11, 0.22, 0.33], 6) // 0.237627

Other solutions

Some functions are defined first and called later in the methods - e.g. sum is used to add numbers together in the methods following it.

const sum = arr => arr.reduce((total, num) => total + num, 0);

const product = arr => arr.reduce((total, num) => total * num, 1);

const mean = arr => sum(arr) / arr.length;

const withPrecision = (num, prec) =>
  typeof prec === 'number' ? Number(num.toFixed(prec)) : num;

Math.avg = (arr, prec) => withPrecision(mean(arr), prec);

Math.qAvg = (arr, prec) =>
  withPrecision(Math.sqrt(mean(arr.map(num => num ** 2))), prec);

Math.hAvg = (arr, prec) =>
  withPrecision(1 / mean(arr.map(num => 1 / num)), prec);

Math.gAvg = (arr, prec) =>
  withPrecision(Math.nthRt(product(arr), arr.length), prec);

Math.nthRt = (num, root, prec) =>
  withPrecision(Math.exp((1 / root) * Math.log(num)), prec);

//Pustur

This solution uses a function, f to determine if a p argument is passed into the method. If it is, then the +x.toFixed(p) method is called.

f = (x,p) => isNaN(p) ? x : +x.toFixed(p)

Math.avg  = (N,p) => f(eval(N.join`+`) / N.length, p)
Math.qAvg = (N,p) => f((N.reduce((a,b) => a + b * b, 0) / N.length) ** .5, p)
Math.hAvg = (N,p) => f(N.length / N.reduce((a,b) => a + 1 / b, 0), p)
Math.gAvg = (N,p) => Math.nthRt(eval(N.join`*`), N.length, p)

Math.nthRt = (n,r,p) => f(Math.exp(1 / r * Math.log(n)), p)
//xAlien95