Typescript で Map のキーをタプルにしたいとき
自分用メモ. Typescript 使っていると, Map オブジェクトのキーにタプルを指定するとうまく動かなかったので
調べてみると, どうやらキー値の比較に ===
を使っているのでダメなよう.
とりあえず, これができないと困るので以下のような簡易 Map を実装した.
そのうち Map の残りのメソッドに対応する・・かも.
class TupleMap {
private _values: Object = {};
protected _clear(): void {
this._values = {};
}
protected _get(key: Array<any>): any {
if (key == null || key.length == 0) {
return false;
}
let result: any = this._values[key[0]];
for (let i = 1; i < key.length; i++) {
if (result == null) {
break;
}
result = result[key[i]];
}
return result;
}
protected _has(key: Array<any>): boolean {
return this._get(key) != null;
}
private _zip<T1, T2>(a1: T1[], a2: T2[]): [T1, T2][] {
let result: [T1, T2][] = [];
for (let i = 0; i < a1.length; i++) {
result.push([a1[i], a2[i]]);
}
return result;
}
protected _delete(key: Array<any>): boolean {
const hasResult = this._has(key);
if (!hasResult) {
return false;
}
let stack: Object[] = [this._values];
let result: any = this._values[key[0]];
for (let i = 1; i < key.length; i++) {
stack.push(result);
result = result[key[i]];
}
// 削除
let objs = this._zip(key, stack).reverse();
delete objs[0][1][objs[0][0]]; // リーフノードは問答無用で削除
for (let [k, o] of objs.slice(1, objs.length)) {
// キーに対応するオブジェクトが空の場合には削除
if (Object.keys(o[k]).length == 0) {
delete o[k];
} else {
break;
}
}
return true;
}
protected _set(key: Array<any>, value: any): this {
if (key == null || key.length == 0) {
throw new Error("invalid key given");
}
if (!(key[0] in this._values)) {
this._values[key[0]] = {};
}
let obj = this._values[key[0]];
for (let part of key.slice(1, key.length)) {
if (!(part in obj)) {
obj[part] = {};
}
obj = obj[part];
}
obj[key[key.length - 1]] = value;
return this;
}
}
interface TupleInterface<T, U> {
clear(): void;
get(key: T): U;
has(key: T): boolean;
delete(key: T): boolean;
set(key: T, value: U): this;
}
export class Tuple2Map<T1, T2, U> extends TupleMap
implements TupleInterface<[T1, T2], U> {
public clear(): void {
this._clear();
}
public get(key: [T1, T2]): U {
return this._get(key);
}
public has(key: [T1, T2]): boolean {
return this._has(key);
}
public delete(key: [T1, T2]): boolean {
return this._delete(key);
}
public set(key: [T1, T2], value: U): this {
return this._set(key, value);
}
}
export class Tuple3Map<T1, T2, T3, U> extends TupleMap
implements TupleInterface<[T1, T2, T3], U> {
public clear(): void {
this._clear();
}
public get(key: [T1, T2, T3]): U {
return this._get(key);
}
public has(key: [T1, T2, T3]): boolean {
return this._has(key);
}
public delete(key: [T1, T2, T3]): boolean {
return this._delete(key);
}
public set(key: [T1, T2, T3], value: U): this {
return this._set(key, value);
}
}
export class Tuple4Map<T1, T2, T3, T4, U> extends TupleMap
implements TupleInterface<[T1, T2, T3, T4], U> {
public clear(): void {
this._clear();
}
public get(key: [T1, T2, T3, T4]): U {
return this._get(key);
}
public has(key: [T1, T2, T3, T4]): boolean {
return this._has(key);
}
public delete(key: [T1, T2, T3, T4]): boolean {
return this._delete(key);
}
public set(key: [T1, T2, T3, T4], value: U): this {
return this._set(key, value);
}
}
export class Tuple5Map<T1, T2, T3, T4, T5, U> extends TupleMap
implements TupleInterface<[T1, T2, T3, T4, T5], U> {
public clear(): void {
this._clear();
}
public get(key: [T1, T2, T3, T4, T5]): U {
return this._get(key);
}
public has(key: [T1, T2, T3, T4, T5]): boolean {
return this._has(key);
}
public delete(key: [T1, T2, T3, T4, T5]): boolean {
return this._delete(key);
}
public set(key: [T1, T2, T3, T4, T5], value: U): this {
return this._set(key, value);
}
}