All files index.ts

100% Statements 45/45
100% Branches 22/22
100% Functions 9/9
100% Lines 38/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98                                            1x 27x         28x 49x   27x 27x 27x     1x 4x     1x 1x     1x 9x     1x 5x         1x       11x 2x     9x 4x     5x     36x 9x 9x     1x 72x 120x 120x 2x     118x   118x 55x 55x     63x 38x 38x   25x       1x  
export interface Options<K> {
  keys?: K[]; // only these keys will be transposed
}
 
type TransposedValues<T, K extends keyof T> = {
  [V in T[K] extends string | number | symbol ? T[K] : never]: T[] | undefined;
};
 
export type Transposed<T, K extends keyof T> = {
  [Key in keyof T]: TransposedValues<T, K>;
};
 
/**
 * If T is not never, use T. Otherwise, infer the type from the keys K
 */
type InferType<T, K extends keyof any> = [T] extends [never]
  ? [K] extends [never]
    ? any
    : { [Key in K]: any } & { [Key in string | number | symbol]: any }
  : T;
 
 
export class OatyArray<T = never, K extends keyof T = keyof T> {
  private _transposed = {} as Transposed<InferType<T, K>, K>;
  private _data: InferType<T, K>[];
  private _options: Options<K>;
 
  constructor(
    data: readonly InferType<T, K>[] = [],
    options: Options<K> = {}
  ) {
    this._data = [...data];
    this._options = options;
    this.transpose(this._data);
  }
 
  get keys(): K[] {
    return (this._options.keys ?? Object.keys(this._transposed)) as K[]
  }
 
  get length(): number {
    return this._data.length
  }
 
  get data(): InferType<T, K>[] {
    return this._data
  }
 
  get transposed(): Transposed<InferType<T, K>, K> {
    return this._transposed
  }
 
  public get<KN extends K>(keyName: KN): TransposedValues<InferType<T, K>, KN>;
  public get<KN extends K>(keyName: KN, keyValue: InferType<T, K>[KN]): InferType<T, K>[] | undefined;
  public get<KN extends K>(
    keyName: KN, 
    keyValue?: InferType<T, K>[KN]
  ): TransposedValues<InferType<T, K>, KN> | InferType<T, K>[] | undefined {
    if (this._transposed[keyName] === undefined) {
      throw new ReferenceError(`The key '${keyName}' has not been transposed`)
    }
 
    if (keyValue === undefined) {
      return this._transposed[keyName]
    }
 
    return this._transposed[keyName][keyValue as keyof TransposedValues<InferType<T, K>, KN>]
  }
 
  public push(...data: readonly InferType<T, K>[]) {
    this.transpose(data)
    return this._data.push(...data)
  }
 
  private transpose(data: readonly InferType<T, K>[]) {
    for (const datum of data) {
      for (const key of (this._options.keys ?? Object.keys(datum) as (keyof typeof datum)[])) {
        if (datum[key] === undefined) {
          continue	
        }
        
        const searchKey = datum[key] as keyof TransposedValues<InferType<T, K>, K>;
 
        if (this._transposed[key] === undefined) {
          this._transposed[key] = { [searchKey]: [datum] } as TransposedValues<InferType<T, K>, K>
          continue
        }
 
        if (this._transposed[key][searchKey] === undefined) {
          this._transposed[key][searchKey] = [datum]
          continue
        }
        this._transposed[key][searchKey]!.push(datum)
      }
    }
  }
}