/** * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal */ import Column from './column' import { sortArray } from './sort' type Table = { readonly _rowCount: number, readonly _columns: ReadonlyArray } & Table.Columns /** An immutable table */ namespace Table { export type Schema = { [field: string]: Column.Type } export type Columns = { [C in keyof S]: Column } export type Row = { [C in keyof S]: S[C]['T'] } export type Arrays = { [C in keyof S]: ArrayLike } export type PartialTable = { readonly _rowCount: number, readonly _columns: ReadonlyArray } & { [C in keyof S]?: Column } export function pickColumns(schema: S, table: PartialTable, guard: Partial> = {}): Table { const ret = Object.create(null); const keys = Object.keys(schema); ret._rowCount = table._rowCount; ret._columns = keys; for (const k of keys) { if (!!table[k]) ret[k] = table[k]; else if (!!guard[k]) ret[k] = guard[k]; else throw Error(`Cannot find column '${k}'.`); } return ret; } export function ofColumns = Table>(columns: Columns): R { const _columns = Object.keys(columns); const _rowCount = columns[_columns[0]].rowCount; return { _rowCount, _columns, ...(columns as any) }; } export function ofRows = Table>(schema: Schema, rows: ArrayLike>): R { const ret = Object.create(null); const rowCount = rows.length; const columns = Object.keys(schema); ret._rowCount = rowCount; ret._columns = columns; for (const k of columns) { (ret as any)[k] = Column.ofLambda({ rowCount, type: schema[k], value: r => rows[r][k], valueKind: r => typeof rows[r][k] === 'undefined' ? Column.ValueKind.NotPresent : Column.ValueKind.Present }) } return ret as R; } export function ofArrays = Table>(schema: Schema, arrays: Arrays): R { const ret = Object.create(null); const columns = Object.keys(schema); ret._rowCount = arrays[columns[0]].length; ret._columns = columns; for (const k of columns) { (ret as any)[k] = Column.ofArray({ array: arrays[k], type: schema[k] }) } return ret as R; } export function view(table: Table, schema: R, view: ArrayLike) { const ret = Object.create(null); const columns = Object.keys(schema); ret._rowCount = view.length; ret._columns = columns; for (const k of columns) { (ret as any)[k] = Column.view(table[k], view); } return ret as Table; } /** Sort and return a new table */ export function sort, S extends Schema>(table: T, cmp: (i: number, j: number) => number) { const indices = new Int32Array(table._rowCount); for (let i = 0, _i = indices.length; i < _i; i++) indices[i] = i; sortArray(indices, (_, i, j) => cmp(i, j)); let isIdentity = true; for (let i = 0, _i = indices.length; i < _i; i++) { if (indices[i] !== i) { isIdentity = false; break; } } if (isIdentity) return table; const ret = Object.create(null); ret._rowCount = table._rowCount; ret._columns = table._columns; for (const c of table._columns) { ret[c] = Column.view((table as any)[c], indices, false); } return ret; } } export default Table