Last active
June 18, 2025 15:24
-
-
Save ChunMinChang/60750159667af9197984a3cb70364c5e to your computer and use it in GitHub Desktop.
A simple CircularQueue implementation and its application for calculating rolling average
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CumulativeAverage { | |
constructor() { | |
this.num = 0; | |
this.avg = 0; | |
} | |
get average() { | |
return this.avg; | |
} | |
get numberOfValues() { | |
return this.num; | |
} | |
update(value) { | |
this.num += 1; | |
this.avg = this.avg + (value - this.avg) / this.num; | |
return this.avg; | |
} | |
} | |
class RollingAverage { | |
constructor(length) { | |
// See CircularQueue in circular-queue.js | |
this.queue = new CircularQueue(length); | |
this.avg = 0; | |
} | |
get average() { | |
return this.avg; | |
} | |
get numberOfValues() { | |
return this.queue.size; | |
} | |
update(value) { | |
if (!this.queue.isFull) { | |
// cumulative average in this case | |
this.queue.enqueue(value); | |
this.avg = this.avg + (value - this.avg) / this.queue.size; | |
} else { | |
let oldest = this.queue.dequeue(); | |
this.queue.enqueue(value); | |
this.avg = this.avg + (value - oldest) / this.queue.size; | |
} | |
return this.avg; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CircularQueue { | |
constructor(capacity) { | |
if (typeof capacity !== "number" || capacity < 1) { | |
throw new Error("Please specify a valid capacity"); | |
} | |
this.buffer = new Array(capacity + 1); | |
this.readIndex = 0; // index for next read | |
this.writeIndex = 0; // index for next write | |
} | |
get capacity() { | |
return this.buffer.length - 1; | |
} | |
get isEmpty() { | |
return this.readIndex == this.writeIndex; | |
} | |
get isFull() { | |
return (this.writeIndex + 1) % this.buffer.length == this.readIndex; | |
} | |
get size() { | |
if (this.writeIndex >= this.readIndex) { | |
return this.writeIndex - this.readIndex; | |
} | |
return this.writeIndex + this.buffer.length - this.readIndex; | |
} | |
get data() { | |
if (this.writeIndex >= this.readIndex) { | |
return this.buffer.slice(this.readIndex, this.writeIndex); | |
} | |
return this.buffer | |
.slice(this.readIndex) | |
.concat(this.buffer.slice(0, this.writeIndex)); | |
} | |
enqueue(data) { | |
if (this.isFull) { | |
throw new Error("CircularBuffer is full!"); | |
} | |
this.buffer[this.writeIndex] = data; | |
this.writeIndex = (this.writeIndex + 1) % this.buffer.length; | |
} | |
dequeue() { | |
if (this.isEmpty) { | |
return undefined; | |
} | |
const data = this.buffer[this.readIndex]; | |
this.readIndex = (this.readIndex + 1) % this.buffer.length; | |
return data; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment