Skip to content

Instantly share code, notes, and snippets.

@laat
Last active April 25, 2018 21:26
Show Gist options
  • Save laat/24f872d2b9304b12e84fd8f69a16dc77 to your computer and use it in GitHub Desktop.
Save laat/24f872d2b9304b12e84fd8f69a16dc77 to your computer and use it in GitHub Desktop.
CaseInsensitiveMap
import CaseInsensitiveMap from './CaseInsensitiveMap';
describe('CaseInsensitiveMap', () => {
it('should be able to construct', () => {
const map: Map<string, string> = new CaseInsensitiveMap<string>();
});
it('should be able to construct with values', () => {
const map: Map<string, string> = new CaseInsensitiveMap<string>([['key', 'value'], ['key2', 'value2']]);
});
it('has case insensitive keys', () => {
const map: Map<string, string> = new CaseInsensitiveMap<string>([['KEY', 'value']]);
expect(map.has('key')).toBeTruthy();
expect(map.has('KEY')).toBeTruthy();
expect(map.has('KeY')).toBeTruthy();
});
it('can delete case insensitive', () => {
const map: Map<string, string> = new CaseInsensitiveMap<string>([['KEY', 'value']]);
expect(map.has('key')).toBeTruthy();
expect(map.delete('key')).toBeTruthy();
expect(map.delete('key')).toBeFalsy();
expect(map.has('key')).toBeFalsy();
expect(map.has('KEY')).toBeFalsy();
});
it('can set/get case insensitive', () => {
const map: Map<string, string> = new CaseInsensitiveMap<string>();
map.set('KeY', 'value');
expect(map.get('key')).toEqual('value');
});
it('returns the original keys used. in their proper casing. keys()', () => {
const map: Map<string, string> = new CaseInsensitiveMap<string>();
map.set('KeY', 'value');
map.set('Foo', 'value');
map.set('bAAr', 'value');
expect(Array.from(map.keys())).toEqual(['KeY', 'Foo', 'bAAr']);
});
it('returns the original keys used. in their proper casing. entries()', () => {
const map: Map<string, string> = new CaseInsensitiveMap<string>();
map.set('KeY', 'value');
map.set('Foo', 'value');
map.set('bAAr', 'value');
expect(Array.from(map.entries())).toEqual([['KeY', 'value'], ['Foo', 'value'], ['bAAr', 'value']]);
});
it('gives the original keys in forEach()', () => {
const map: Map<string, string> = new CaseInsensitiveMap<string>();
map.set('KeY', 'value');
map.set('Foo', 'value');
map.set('bAAr', 'value');
let found: string[] = [];
map.forEach((value, key) => {
found.push(key);
});
expect(found).toEqual(['KeY', 'Foo', 'bAAr']);
});
});
export default class CaseInsensitiveMap<T> implements Map<string, T> {
private keyMap_: Map<string, string>;
private valueMap_: Map<string, T>;
constructor(entries?: ReadonlyArray<[string, T]>) {
this.keyMap_ = new Map();
this.valueMap_ = new Map();
if (entries) {
entries.forEach(([key, value]) => {
this.keyMap_.set(key.toLowerCase(), key);
this.valueMap_.set(key.toLowerCase(), value);
});
}
}
has(key: string) {
return this.keyMap_.has(key.toLowerCase());
}
set(key: string, value: T) {
const lKey = key.toLowerCase();
if (!this.keyMap_.has(lKey)) {
this.keyMap_.set(lKey, key);
}
this.valueMap_.set(lKey, value);
return this;
}
get(key: string) {
const lKey = key.toLowerCase();
return this.valueMap_.get(lKey);
}
delete(key: string) {
const lKey = key.toLowerCase();
this.keyMap_.delete(lKey);
return this.valueMap_.delete(lKey);
}
clear() {
this.keyMap_.clear();
this.valueMap_.clear();
}
keys() {
return this.keyMap_.values();
}
values() {
return this.valueMap_.values();
}
get size() {
return this.keyMap_.size;
}
entries() {
const it = this.valueMap_.entries();
const keyMap = this.keyMap_;
return {
next() {
const res = it.next();
res.value = res.value ? [keyMap.get(res.value[0])!, res.value[1]] : res.value;
return res;
},
[Symbol.iterator]() {
return this;
}
};
}
[Symbol.iterator]() {
return this.entries();
}
forEach(callbackfn: (value: T, key: string, map: Map<string, T>) => void, thisArg?: any) {
const keyMap = this.keyMap_;
this.valueMap_.forEach((value, key, map) => callbackfn(value, keyMap.get(key)!, this), thisArg);
}
readonly [Symbol.toStringTag] = "CaseInsensitiveMap" as "Map";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment