A Brief Context
I recently gave an interview in a company where I was asked this particular question. After the interview was over, the HR gave the feedback that my solution (is correct programmatically) but is monolithic and not scalable. The interview was for frontend developer 2.
Question
Suppose there is a company which does some analytics work and returns data. The company (let's say X) gets a stream of numbers and it calculates average at that instant for all numbers received and returns the average. There could be other companies (let's say Y) which can ask for the average.
Data: 1 3 5 7 9
Avg: 1 2 3 4 5
So, in the above example, the average are calculated by company X.
- After first no, average is 1,
- After second no (i.e. 3), average is 2 ((3+1)/2) and so on
At any point in time, companies are only concerned about average at that time (basically, if average is asked after data value 5, you only need to return 3)
The geeksforgeeks link for problem is https://www.geeksforgeeks.org/average-of-a-stream-of-numbers/
Complexity
The solution needs to be O(1) time and O(1) space
My solution:
I wrote a closure in Javascript
to save the current average and total number of values encountered so far. Then wrote another function to call it by passing some parameters. The parameters decide whether I should be updating the average or returning it.
function analytics() {
const data = {
avg: 0, //the average at that instant
totalPoints: 0 //total number of values encountered till that point
}
return function(value) {
//if value is not present, return the avg
if (!value) {
return data.avg
}
const newTotal = data.avg * data.totalPoints + value
const newTotalP = data.totalPoints + 1
//update the value for next usage
data.avg = newTotal / newTotalP
data.totalPoints = newTotalP
return data.avg
}
}
fn = analytics()
/*
param = {
type: //Company Names
value: //Value to update
}
*/
function y({
type,
value
}) {
if (["Y"].indexOf(type) >= 0) {
return fn()
} else if (["X"].indexOf(type) >= 0) {
return fn(value)
}
}
console.log("get avg", y({
type: "Y"
}))
console.log("update avg", y({
type: "X",
value: 1
}))
console.log("update avg", y({
type: "X",
value: 3
}))
console.log("update avg", y({
type: "X",
value: 5
}))
console.log("get avg", y({
type: "Y"
}))
console.log("update avg", y({
type: "X",
value: 7
}))
console.log("update avg", y({
type: "X",
value: 9
}))
console.log("get avg", y({
type: "Y"
}))
My concerns/doubts:
- What did I miss here so that solution is not scalable
- Any use cases when it might cause issues
- How can it be re-written to make it scalable
Things the can be assumed won't cause any issues
- Need not worry about precision issues while calculating the average
- Need not worry about issues due to Number datatype not able to accomodate very large values
- Data validation issues
- Variable names etc (I re-wrote the solution again for codereview, so some names are not correct)
Hope, I am able to explain the situation properly and would like to get more insights here. Also, let me know if anyone needs any further explanation or something is missing
return total/numEntries
. \$\endgroup\$