# All files / utils getLatestValues.ts

83.92% Statements 47/56
65.51% Branches 19/29
83.33% Functions 10/12
82.69% Lines 43/52

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
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    1282x
    2x
    2x

    2x
    4x
    4x

    14x
    14x

    14x
    8x
    8x
    6x

    4x

    2x

    2x
    9x
    9x

    9x

    2x

    2x
    6x
    9x

    5x

    6x
    1x

    5x

    5x

    5x
    3x
    2x

    5x

    5x
    14x

    14x

    10x

    10x

    4x

    5x

    5x
    4x

    5x
    4x

    5x

     import {range} from './range'
    import {isString} from './isString'
    import {flatMap} from './flatMap'
    import {Table, NumericColumnData} from '../types'

    /*
      Return a list of the maximum elements in `xs`, where the magnitude of each
      element is computed using the passed function `d`.
    */
    const maxesBy = <X>(xs: X[], d: (x: X) => number): X[] => {
      let maxes = []
      let maxDist = -Infinity

      for (const x of xs) {
        const dist = d(x)

        if (dist > maxDist) {
          maxes = [x]
          maxDist = dist
        } else Iif (dist === maxDist && dist !== -Infinity) {
          maxes.push(x)
        }
      }

      return maxes
    }

    const EXCLUDED_COLUMNS = new Set([
      '_start',
      '_stop',
      '_time',
      'table',
      'result',
      '',
    ])

    /*
      Determine if the values in a column should be considered in `latestValues`.
    */
    const isValueCol = (table: Table, colKey: string): boolean => {
      const columnType = table.getColumnType(colKey)
      const columnName = table.getColumnName(colKey)

      return (
        (columnType === 'number' ||
          columnType === 'time' ||
          columnType === 'string') &&
        !EXCLUDED_COLUMNS.has(columnName)
      )
    }

    /*
      We sort the column keys that we pluck latest values from, so that:

      - Columns named `_value` have precedence
      - The returned latest values are in a somewhat stable order
    */
    const sortTableKeys = (keyA: string, keyB: string): number => {
      if (keyA.includes('_value')) {
        return -1
      } else if (keyB.includes('_value')) {
        return 1
      } else {
        return keyA.localeCompare(keyB)
      }
    }

    /*
      Return a list of the most recent numeric values present in a `Table`.

      This utility searches any numeric column to find values, and uses the `_time`
      column as their associated timestamp.

      If the table only has one row, then a time column is not needed.
    */
    export const getLatestValues = (table: Table): number[] => {
      const valueColsData = table.columnKeys
        .filter(k => isValueCol(table, k))
        .sort((a, b) => sortTableKeys(a, b))
        .map(k => table.getColumn(k)) as number[][]

      if (!valueColsData.length) {
        return []
      }

      const columnKeys = table.columnKeys

      // Fallback to `_stop` column if `_time` column missing otherwise return empty array.
      let timeColData: NumericColumnData = []

      if (columnKeys.includes('_time')) {
        timeColData = table.getColumn('_time', 'number')
      } else Iif (columnKeys.includes('_stop')) {
        timeColData = table.getColumn('_stop', 'number')
      }
      Iif (!timeColData && table.length !== 1) {
        return []
      }

      const d = (i: number) => {
        const time = timeColData[i]

        if (
          time &&
          valueColsData.some(colData => {
            return Number.isFinite(colData[i]) || isString(colData[i])
          })
        ) {
          return time
        }

        return -Infinity
      }

      const latestRowIndices =
        table.length === 1 ? [0] : maxesBy(range(0, table.length), d)

      const latestValues = flatMap(latestRowIndices, i =>
        valueColsData.map(colData => colData[i])
      )

      const definedLatestValues = latestValues.filter(
        x => Number.isFinite(x) || isString(x)
      )

      return definedLatestValues
    }