table.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /**
  2. * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import Column from './column'
  7. import { sortArray } from '../util/sort'
  8. /** A collection of columns */
  9. type Table<Schema extends Table.Schema> = {
  10. readonly _rowCount: number,
  11. readonly _columns: ReadonlyArray<string>,
  12. readonly _schema: Schema
  13. } & Table.Columns<Schema>
  14. /** An immutable table */
  15. namespace Table {
  16. export type Schema = { [field: string]: Column.Schema }
  17. export type Columns<S extends Schema> = { [C in keyof S]: Column<S[C]['T']> }
  18. export type Row<S extends Schema> = { [C in keyof S]: S[C]['T'] }
  19. export type Arrays<S extends Schema> = { [C in keyof S]: ArrayLike<S[C]['T']> }
  20. export type PartialTable<S extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & { [C in keyof S]?: Column<S[C]['T']> }
  21. export function is(t: any): t is Table<any> {
  22. return t && typeof t._rowCount === 'number' && !!t._columns && !!t._schema;
  23. }
  24. export function pickColumns<S extends Schema>(schema: S, table: PartialTable<S>, guard: Partial<Columns<S>> = {}): Table<S> {
  25. const ret = Object.create(null);
  26. const keys = Object.keys(schema);
  27. ret._rowCount = table._rowCount;
  28. ret._columns = keys;
  29. ret._schema = schema;
  30. for (const k of keys) {
  31. if (!!table[k]) ret[k] = table[k];
  32. else if (!!guard[k]) ret[k] = guard[k];
  33. else throw Error(`Cannot find column '${k}'.`);
  34. }
  35. return ret;
  36. }
  37. export function ofColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, columns: Columns<S>): R {
  38. const _columns = Object.keys(columns);
  39. const _rowCount = columns[_columns[0]].rowCount;
  40. return { _rowCount, _columns, _schema: schema, ...(columns as any) };
  41. }
  42. export function ofUndefinedColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, rowCount: number): R {
  43. const ret = Object.create(null);
  44. const columns = Object.keys(schema);
  45. ret._rowCount = rowCount;
  46. ret._columns = columns;
  47. ret._schema = schema;
  48. for (const k of columns) {
  49. ret[k] = Column.Undefined(rowCount, schema[k])
  50. }
  51. return ret;
  52. }
  53. export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Row<S>>): R {
  54. const ret = Object.create(null);
  55. const rowCount = rows.length;
  56. const columns = Object.keys(schema);
  57. ret._rowCount = rowCount;
  58. ret._columns = columns;
  59. ret._schema = schema;
  60. for (const k of columns) {
  61. (ret as any)[k] = Column.ofLambda({
  62. rowCount,
  63. schema: schema[k],
  64. value: r => rows[r][k],
  65. valueKind: r => typeof rows[r][k] === 'undefined' ? Column.ValueKind.NotPresent : Column.ValueKind.Present
  66. })
  67. }
  68. return ret as R;
  69. }
  70. export function ofArrays<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, arrays: Arrays<S>): R {
  71. const ret = Object.create(null);
  72. const columns = Object.keys(schema);
  73. ret._rowCount = arrays[columns[0]].length;
  74. ret._columns = columns;
  75. ret._schema = schema;
  76. for (const k of columns) {
  77. (ret as any)[k] = Column.ofArray({ array: arrays[k], schema: schema[k] })
  78. }
  79. return ret as R;
  80. }
  81. export function view<S extends R, R extends Schema>(table: Table<S>, schema: R, view: ArrayLike<number>) {
  82. const ret = Object.create(null);
  83. const columns = Object.keys(schema);
  84. ret._rowCount = view.length;
  85. ret._columns = columns;
  86. ret._schema = schema;
  87. for (const k of columns) {
  88. (ret as any)[k] = Column.view(table[k], view);
  89. }
  90. return ret as Table<R>;
  91. }
  92. export function columnToArray<S extends Schema>(table: Table<S>, name: keyof S, array?: Column.ArrayCtor<any>) {
  93. (table as Columns<S>)[name] = Column.asArrayColumn((table as Columns<S>)[name], array);
  94. }
  95. /** Sort and return a new table */
  96. export function sort<T extends Table<S>, S extends Schema>(table: T, cmp: (i: number, j: number) => number) {
  97. const indices = new Int32Array(table._rowCount);
  98. for (let i = 0, _i = indices.length; i < _i; i++) indices[i] = i;
  99. sortArray(indices, (_, i, j) => cmp(i, j));
  100. let isIdentity = true;
  101. for (let i = 0, _i = indices.length; i < _i; i++) {
  102. if (indices[i] !== i) {
  103. isIdentity = false;
  104. break;
  105. }
  106. }
  107. if (isIdentity) return table;
  108. const ret = Object.create(null);
  109. ret._rowCount = table._rowCount;
  110. ret._columns = table._columns;
  111. ret._schema = table._schema;
  112. for (const c of table._columns) {
  113. ret[c] = Column.view((table as any)[c], indices, false);
  114. }
  115. return ret;
  116. }
  117. export function areEqual<T extends Table<Schema>>(a: T, b: T) {
  118. if (a._rowCount !== b._rowCount) return false;
  119. if (a._columns.length !== b._columns.length) return false;
  120. for (const c of a._columns) {
  121. if (!b[c]) return false;
  122. }
  123. for (const c of a._columns) {
  124. if (!Column.areEqual(a[c], b[c])) return false;
  125. }
  126. return true;
  127. }
  128. /** Allocate a new object with the given row values. */
  129. export function getRow<S extends Schema>(table: Table<S>, index: number) {
  130. const row: Row<S> = Object.create(null);
  131. const { _columns: cols } = table;
  132. for (let i = 0; i < cols.length; i++) {
  133. const c = cols[i];
  134. row[c] = table[c].value(index);
  135. }
  136. return row;
  137. }
  138. export function getRows<S extends Schema>(table: Table<S>) {
  139. const ret: Row<S>[] = [];
  140. const { _rowCount: c } = table;
  141. for (let i = 0; i < c; i++) {
  142. ret[i] = getRow(table, i);
  143. }
  144. return ret;
  145. }
  146. }
  147. export default Table