# All files / utils fromRows.ts

96.96% Statements 64/66
86.95% Branches 20/23
100% Functions 8/8
96.29% Lines 52/54

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
    99
    100
    101
    102
    103
    104
    105
    106
    1071x
    1x

    1x

    5x
    5x

    5x
    13x

    38x
    38x
    3x

    35x
    14x

    35x

    35x
    12x
    12x

    23x
    22x

    34x
    1x

    33x

    33x

    12x
    12x

    3x
    12x
    12x

    3x

    1x
    34x

    6x

    15x

    6x

    7x

    1x
    23x

    12x

    4x

    3x

    4x
    3x

    1x

    1x
    1x

    1x
    1x

    1x

    1x
    1x

    1x
    1x

    1x
     import {Table, ColumnType, ErrorName} from '../types'
    import {newTable} from './newTable'

    export const fromRows = <T extends object>(
      rows: T[],
      schema?: {[K in keyof T]?: ColumnType}
    ): Table => {
      const columns: {[columnKey: string]: any[]} = {}
      const resolvedSchema: {[columnKey: string]: ColumnType} = {}

      for (let i = 0; i < rows.length; i++) {
        const row = rows[i]

        for (const key of Object.keys(row)) {
          if (schema && !schema[key]) {
            continue
          }

          if (!columns[key]) {
            columns[key] = new Array(i).fill(undefined)
          }

          let value, columnType

          if (schema) {
            columnType = schema[key]
            value = parseValue(row[key], columnType)
          } else {
            columnType = inferColumnType(row[key])
            value = parseValue(row[key], columnType)
          }

          if (resolvedSchema[key] && resolvedSchema[key] !== columnType) {
            throw new SchemaMismatchError(key, resolvedSchema[key], columnType)
          } else {
            resolvedSchema[key] = columnType
          }

          columns[key][i] = value
        }
      }

      for (const column of Object.values(columns)) {
        column.length = rows.length
      }

      const table = Object.entries(columns).reduce(
        (table, [key, values]) =>
          table.addColumn(key, 'system', resolvedSchema[key], values),
        newTable(rows.length)
      )

      return table
    }

    const parseValue = (value: any, to: ColumnType) => {
      switch (to) {
        case 'time':
          return new Date(value).getTime()
        case 'number':
          return parseFloat(value)
        case 'boolean':
          return !!value
        case 'string':
          return String(value)
        default:
          const uhOh: never = to

          throw new Error(`unexpected ColumnType "${uhOh}"`)
      }
    }

    const inferColumnType = (value: any): ColumnType => {
      switch (typeof value) {
        case 'number':
          return 'number'
        case 'string':
          return 'string'
        case 'boolean':
          return 'boolean'
      }

      if (value instanceof Date) {
        return 'time'
      }

      throw new UnknownColumnTypeError(value)
    }

    class UnknownColumnTypeError extends Error {
      constructor(value) {
        super(...arguments)

        this.name = ErrorName.UnknownColumnTypeError
        this.message = `could not infer ColumnType of value "${value}"`
      }
    }

    class SchemaMismatchError extends Error {
      constructor(columnKey, firstColumnType, secondColumnType) {
        super(...arguments)

        this.name = ErrorName.SchemaMismatchError
        this.message = `ColumnType for column "${columnKey}" cannot be both "${firstColumnType}" and "${secondColumnType}"`
      }
    }