Null isTrue
Null isTrue

Reputation: 1916

Inaccurate Average reported from JavaScript Function

I have an average() method to calculate the average between two values. The averages are coming off by a "hair" of a decimal value.

const measurements = [ 
  { timestamp: '2015-09-01T16:00:00.000Z',
    temperature: 27.1,
    dewPoint: 16.9 
  },
  { timestamp: '2015-09-01T16:10:00.000Z',
    temperature: 27.3,
    dewPoint: 0 
  },
  { timestamp: '2015-09-01T16:20:00.000Z',
    temperature: 27.5,
    dewPoint: 17.1 
   },
   { timestamp: '2015-09-01T16:30:00.000Z',
    temperature: 27.4,
    dewPoint: 17.3 
   },
   { timestamp: '2015-09-01T16:40:00.000Z',
    temperature: 27.2,
    dewPoint: 0 
   },
   { timestamp: '2015-09-01T17:00:00.000Z',
    temperature: 28.1,
    dewPoint: 18.3 
   } 
] 

For sake of conciseness, I'm not gonna share 60 lines of code here:

Assumptions:

Therefore this is the result of the explanation above:


// POSTMAN result

[
    {
        "metric": "dewPoint",
        "stat": "min",
        "value": 16.9
    },
    {
        "metric": "dewPoint",
        "stat": "max",
        "value": 18.3
    },
    {
        "metric": "dewPoint",
        "stat": "average",
        "value": 17.4
    }
]

I want to get the average between the max and min values. i.e. 16.9 & 18.3 which should be 17.6, however, I'm getting 17.4 instead

Here's the one method that has the actual bug.

function averageMetric(measurements, metric) {

  // => metric dewPoint
  // => measurements = the data array in example

  let value = 0
  let measurementsWithMetric = 0
  measurements.forEach(measurement => {
    if (measurement[metric]) {
      value += measurement[metric]
      measurementsWithMetric++
    }
  })
  //=> value = 69.6
  //=> measurementsWithMetric = 4
  const average = value / measurementsWithMetric // Is this the issue?
  // average =  17.4

  return isNaN(average) ? null : Math.round(average * 100) / 100
}

Could you help me understand the issue here and also suggest an ES6 equivalent solution of the method above?

Upvotes: 0

Views: 71

Answers (2)

TheRealOrange
TheRealOrange

Reputation: 115

measurements.forEach(measurement => {
  if (measurement[metric]) {
    value += measurement[metric]
    measurementsWithMetric++
  }
})

is incorrect if you want to get the average of the min and max, (which is the mid range)

Instead, you should try

let min = 10**6, max = -10**6; //unless your dew point is stupid this should be fine
measurements.forEach(measurement => {
  if (measurement[metric]) {
    min = Math.min(measurement[metric], min);
    max = Math.max(measurement[metric], max);
    measurementsWithMetric++
  }
})
let average = (max+min)/2;

Upvotes: 0

P Varga
P Varga

Reputation: 20269

Your function calculates the arithmetic mean, not the mid-range. Here is a function to calculate that:

const measurements = [ 
  {temperature: 27.1, dewPoint: 16.9},
  {temperature: 27.3, dewPoint: 0}, // Isn't this the minimum value though?
  {temperature: 27.5, dewPoint: 17.1},
  {temperature: 27.4, dewPoint: 17.3},
  {temperature: 27.2, dewPoint: 0},
  {temperature: 28.1, dewPoint: 18.3}, 
  {temperature: 28.2} 
];
function averageMetric(meas, metr) {
  const valid = meas.filter(e => e[metr]);
  const min = Math.min(...valid.map(e => e[metr]));
  const max = Math.max(...valid.map(e => e[metr]));
  return (min + max) / 2;
}

console.log(averageMetric(measurements, 'dewPoint'));

Upvotes: 2

Related Questions