mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 08:51:24 +00:00
use ES6 Map if we can (#602)
This commit is contained in:
@@ -1,49 +1,9 @@
|
||||
import { HashMap } from "./HashMap";
|
||||
|
||||
/**
|
||||
* Hash map of arrays which allows to:
|
||||
* - push an item by key: add to array and create array if needed
|
||||
* - remove item by key: remove from array and delete array if needed
|
||||
*/
|
||||
|
||||
export class ArrayHashMap<T> {
|
||||
private store: HashMap<T[]>;
|
||||
|
||||
constructor() {
|
||||
this.store = new HashMap();
|
||||
}
|
||||
|
||||
public has(key: string): boolean {
|
||||
return this.store.has(key);
|
||||
}
|
||||
|
||||
public get(key: string): T[] {
|
||||
return this.store.get(key);
|
||||
}
|
||||
|
||||
public size(): number {
|
||||
return this.store.size();
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.store.clear();
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
return this.store.keys();
|
||||
}
|
||||
|
||||
public delete(key: string): boolean {
|
||||
return this.store.delete(key);
|
||||
}
|
||||
|
||||
public forEach(key: string, iteratorFct: (value: T) => void) {
|
||||
const values = this.store.get(key);
|
||||
if (values) {
|
||||
values.forEach((value) => iteratorFct(value));
|
||||
}
|
||||
}
|
||||
|
||||
export class ArrayHashMap<T> extends Map<string, T[]> {
|
||||
/**
|
||||
* Insert item into array.
|
||||
* If no array, create one.
|
||||
@@ -52,16 +12,8 @@ export class ArrayHashMap<T> {
|
||||
* @param item
|
||||
*/
|
||||
public push(key: string, item: T): void {
|
||||
let itemsArray: T[] = this.store.get(key);
|
||||
if (!itemsArray) {
|
||||
itemsArray = [item];
|
||||
this.store.set(key, itemsArray);
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemsArray.indexOf(item) === -1) {
|
||||
itemsArray.push(item);
|
||||
}
|
||||
const array = this.get(key);
|
||||
array ? array.includes(item) || array.push(item) : this.set(key, [item]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,18 +22,11 @@ export class ArrayHashMap<T> {
|
||||
* @param key
|
||||
* @param itemToRemove
|
||||
*/
|
||||
public remove(key: string, itemToRemove: T) {
|
||||
if (!this.store.has(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemsArray = this.store.get(key);
|
||||
const index = itemsArray.indexOf(itemToRemove);
|
||||
if (index >= 0) {
|
||||
itemsArray.splice(index, 1);
|
||||
if (itemsArray.length === 0) {
|
||||
this.store.delete(key);
|
||||
}
|
||||
public remove(key: string, itemToRemove: T): void {
|
||||
const array = this.get(key);
|
||||
if (array) {
|
||||
const remaining = array.filter((item) => item !== itemToRemove);
|
||||
remaining.length ? this.set(key, remaining) : this.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { HashMap } from "./HashMap";
|
||||
|
||||
describe("HashMap", () => {
|
||||
it("should test if key/val exists", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 123);
|
||||
|
||||
expect(map.has("a")).toBe(true);
|
||||
expect(map.has("b")).toBe(false);
|
||||
});
|
||||
|
||||
it("should get object back", () => {
|
||||
const map = new HashMap<string>();
|
||||
map.set("a", "123");
|
||||
map.set("a", "456");
|
||||
|
||||
expect(map.get("a")).toBe("456");
|
||||
expect(map.get("a")).not.toBe("123");
|
||||
});
|
||||
|
||||
it("should return the right size", () => {
|
||||
const map = new HashMap<string>();
|
||||
map.set("a", "123");
|
||||
map.set("b", "456");
|
||||
|
||||
expect(map.size()).toBe(2);
|
||||
});
|
||||
|
||||
it("should be iterable", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 1);
|
||||
map.set("b", 10);
|
||||
map.set("c", 100);
|
||||
map.set("d", 1000);
|
||||
|
||||
let i = 0;
|
||||
map.forEach((key: string, value: number) => {
|
||||
i += value;
|
||||
});
|
||||
expect(i).toBe(1111);
|
||||
});
|
||||
|
||||
it("should be deleted", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 1);
|
||||
map.set("b", 10);
|
||||
|
||||
expect(map.delete("a")).toBe(true);
|
||||
expect(map.delete("c")).toBe(false);
|
||||
expect(map.has("a")).toBe(false);
|
||||
expect(map.has("b")).toBe(true);
|
||||
});
|
||||
|
||||
it("should clear", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 1);
|
||||
map.clear();
|
||||
expect(map.size()).toBe(0);
|
||||
expect(map.has("a")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return all keys", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 1);
|
||||
map.set("b", 1);
|
||||
expect(map.keys()).toEqual(["a", "b"]);
|
||||
map.clear();
|
||||
expect(map.keys().length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Simple hashmap implementation that doesn't rely on ES6 Map nor polyfills
|
||||
*/
|
||||
export class HashMap<T> {
|
||||
constructor(private container: { [key: string]: T } = {}) {}
|
||||
|
||||
public has(key: string): boolean {
|
||||
return this.container.hasOwnProperty(key);
|
||||
}
|
||||
|
||||
public set(key: string, value: T): void {
|
||||
this.container[key] = value;
|
||||
}
|
||||
|
||||
public get(key: string): T {
|
||||
return this.container[key];
|
||||
}
|
||||
|
||||
public size(): number {
|
||||
return Object.keys(this.container).length;
|
||||
}
|
||||
|
||||
public delete(key: string): boolean {
|
||||
if (this.has(key)) {
|
||||
delete this.container[key];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.container = {};
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
return Object.keys(this.container);
|
||||
}
|
||||
|
||||
public forEach(iteratorFct: (key: string, value: T) => void) {
|
||||
for (const k in this.container) {
|
||||
iteratorFct(k, this.container[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ describe("Object cache", () => {
|
||||
cache.set("b", 2);
|
||||
cache.set("c", 3);
|
||||
cache.set("d", 4);
|
||||
expect(cache.size()).toBe(2);
|
||||
expect(cache.size).toBe(2);
|
||||
});
|
||||
|
||||
it("should remove first added element to keep size at limit", () => {
|
||||
|
||||
@@ -1,56 +1,27 @@
|
||||
import { HashMap } from "./HashMap";
|
||||
|
||||
export class ObjectCache<T> extends HashMap<T> {
|
||||
private keyQueue: string[]; // Last touched key FIFO to purge cache if too big.
|
||||
private maxNbElements: number;
|
||||
|
||||
public constructor(maxNbElements: number) {
|
||||
export class ObjectCache<T> extends Map<string, T> {
|
||||
constructor(private limit: number) {
|
||||
super();
|
||||
this.keyQueue = [];
|
||||
this.maxNbElements = maxNbElements;
|
||||
this.clear();
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
super.clear();
|
||||
this.keyQueue = [];
|
||||
public get(key: string): T | undefined {
|
||||
return this.touch(key);
|
||||
}
|
||||
|
||||
public get(key: string): T {
|
||||
this.markKeyAsTouched(key);
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
public set(key: string, value: T): void {
|
||||
super.set(key, value);
|
||||
|
||||
this.markKeyAsTouched(key);
|
||||
|
||||
if (super.size() > this.maxNbElements && key !== this.keyQueue[0]) {
|
||||
this.reduceCacheSize();
|
||||
public set(key: string, value: T): this {
|
||||
if (this.size === this.limit) {
|
||||
this.delete(this.keys().next().value);
|
||||
}
|
||||
|
||||
return this.touch(key, value), this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate elements to keep the total number below the limit
|
||||
*/
|
||||
private reduceCacheSize(): void {
|
||||
// remove a key
|
||||
const oldKey = this.keyQueue.shift();
|
||||
if (oldKey) {
|
||||
super.delete(oldKey);
|
||||
private touch(key: string, value = super.get(key)) {
|
||||
// Map keeps (re) insertion order according to ES6 spec
|
||||
if (value) {
|
||||
this.delete(key);
|
||||
super.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bubble up this key as new.
|
||||
* @param key
|
||||
*/
|
||||
private markKeyAsTouched(key: string) {
|
||||
const n = this.keyQueue.indexOf(key);
|
||||
if (n > -1) {
|
||||
this.keyQueue.splice(n, 1);
|
||||
}
|
||||
this.keyQueue.push(key);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user