class Counter extends Map {
constructor(iterable = []) {
super();
for (const item of iterable) {
this.increment(item);
}
return new Proxy(this, {
get: (target, prop) => {
if (typeof target[prop] === "function") {
return target[prop].bind(target);
}
if (prop in target) {
return target[prop];
}
const key =
!isNaN(prop) && String(Number(prop)) === prop ? Number(prop) : prop;
return target.get(key) || 0;
},
set: (target, prop, value) => {
const key =
!isNaN(prop) && String(Number(prop)) === prop ? Number(prop) : prop;
target.set(key, value);
return true;
}
});
}
get(key) {
return super.get(key) || 0;
}
increment(key, count = 1) {
this.set(key, this.get(key) + count);
return this;
}
mostCommon({ n = null, normalize = false } = {}) {
return this.sorted({ n, normalize, compare: (a, b) => b[1] - a[1] });
}
leastCommon({ n = null, normalize = false } = {}) {
return this.sorted({ n, normalize, compare: (a, b) => a[1] - b[1] });
}
sorted({
n = null,
normalize = false,
compare = (a, b) => a[0] - b[0]
} = {}) {
let entries = [...this];
entries.sort(compare);
if (n !== null) {
entries = entries.slice(0, n);
}
if (normalize) {
const total = this.total();
return entries.map(([a, b]) => [a, b / total]);
} else {
return entries;
}
}
total() {
let sum = 0;
for (const count of this.values()) {
sum += count;
}
return sum;
}
}