class CSVDecision::Index

Build an index for a decision table with one or more input columns designated as keys @api private

Attributes

columns[R]

@return [Array<Integer>] Array of column indices

hash[R]

@return [Hash] The index hash mapping in input values to one or more data array row indexes.

Public Class Methods

build(table:) click to toggle source

Build the index on the designated number of input columns.

@param table [CSVDecision::Table] Decision table being indexed. @return [CSVDecision::Index] The built index.

# File lib/csv_decision/index.rb, line 16
def self.build(table:)
  # Do we even have an index?
  key_cols = index_columns(columns: table.columns.ins)
  return if key_cols.empty?

  table.index = Index.new(table: table, columns: key_cols)

  # Indexed columns do not need to be scanned
  trim_scan_rows(scan_rows: table.scan_rows, index_columns: table.index.columns)

  table
end
new(table:, columns:) click to toggle source

@param table [CSVDecision::Table] Decision table. @param columns [Array<Index>] Array of column indexes to be indexed.

# File lib/csv_decision/index.rb, line 79
def initialize(table:, columns:)
  @columns = columns
  @hash = {}

  build(table)

  freeze
end
value(current_value, index) click to toggle source

@param current_value [Integer, Array] Current index key value. @param index [Integer] Array row index to be included in the table index entry. @return [Integer, Array] New index key value.

# File lib/csv_decision/index.rb, line 32
def self.value(current_value, index)
  return integer_value(current_value, index) if current_value.is_a?(Integer)

  array_value(current_value, index)

  current_value
end

Private Class Methods

array_value(current_value, index) click to toggle source

Current value is an array of row indexes

# File lib/csv_decision/index.rb, line 61
def self.array_value(current_value, index)
  start_row, end_row = current_value.last

  end_row = start_row if end_row.nil?

  # Is the new row index contiguous with the last start row/end row range?
  end_row + 1 == index ? current_value[-1] = [start_row, index] : current_value << index
end
index_columns(columns:) click to toggle source
# File lib/csv_decision/index.rb, line 45
def self.index_columns(columns:)
  key_cols = []
  columns.each_pair { |col, column| key_cols << col if column.indexed }

  key_cols
end
integer_value(current_value, index) click to toggle source

Current value is a row index integer

# File lib/csv_decision/index.rb, line 54
def self.integer_value(current_value, index)
  # Is the new row index contiguous with the last start row/end row range?
  current_value + 1 == index ? [[current_value, index]] : [current_value, index]
end
trim_scan_rows(scan_rows:, index_columns:) click to toggle source
# File lib/csv_decision/index.rb, line 40
def self.trim_scan_rows(scan_rows:, index_columns:)
  scan_rows.each { |scan_row| scan_row.constants = scan_row.constants - index_columns }
end

Private Instance Methods

build(table) click to toggle source
# File lib/csv_decision/index.rb, line 90
def build(table)
  table.each do |row, index|
    key = build_key(row: row)

    current_value = @hash.key?(key)
    @hash[key] = current_value ? Index.value(@hash[key], index) : index
  end
end
build_key(row:) click to toggle source
# File lib/csv_decision/index.rb, line 99
def build_key(row:)
  if @columns.count == 1
    row[@columns[0]]
  else
    @columns.map { |col| row[col] }
  end
end