class CSVDecision::Result
Accumulate the matching row(s) into a result hash. @api private
Attributes
@return [Hash{Symbol=>Object}, Hash{Integer=>Object}] The decision result hash containing
both result values and if: columns, which eventually get evaluated and removed.
@return [Boolean] Returns true if this is a multi-row result
@return [Hash{Index=>Dictionary::Entry}] Output columns.
@return [nil, true] Set to true if the table has output functions.
Public Class Methods
(see Decision.initialize)
# File lib/csv_decision/result.rb, line 25 def initialize(table:) @outs = table.columns.outs @outs_functions = table.outs_functions @table = table end
Public Instance Methods
Accumulate the outs into arrays of values. @param row [Array] @return [void]
# File lib/csv_decision/result.rb, line 58 def accumulate_outs(row) @outs.each_pair { |col, column| add_cell(column_name: column.name, cell: row[col]) } end
Common case for building a single row result is just copying output column values to the final result hash. @param row [Array] @return [void]
# File lib/csv_decision/result.rb, line 51 def add_outs(row) @outs.each_pair { |col, column| @attributes[column.name] = row[col] } end
Evaluate the cell proc using the partial result calculated so far.
@param proc [Matchers::Pro] @param column_name [Symbol, Integer] @param index [Integer]
# File lib/csv_decision/result.rb, line 91 def eval_cell_proc(proc:, column_name:, index:) @attributes[column_name][index] = proc.function[partial_result(index)] end
Evaluate the output columns, and use them to start building the final result, along with the partial result required to evaluate functions.
@param row [Array] @return (see final)
# File lib/csv_decision/result.rb, line 76 def eval_outs(row) # Set the constants first, in case the functions refer to them eval_outs_constants(row: row) # Then evaluate the procs, left to right eval_outs_procs(row: row) final_result end
Derive the final result. @return [Hash{Symbol=>Object}]
# File lib/csv_decision/result.rb, line 64 def final_result # If there are no if: columns, then nothing needs to be filtered out of this result hash. return @attributes if @table.columns.ifs.empty? @multi_result ? multi_row_result : single_row_result end
Initialize the object for new input data.
@param data [Hash{Symbol=>Object}] Input
data hash. @return [void]
# File lib/csv_decision/result.rb, line 35 def input(data) # Attributes hash contains the output decision key value pairs @attributes = {} @multi_result = false # Partial result always copies in the input hash for calculating output functions. # Note that these input key values will not be mutated, as output columns can never # have the same symbol as an input hash key. # However, the rest of this hash is mutated as output column evaluation results # are accumulated. @partial_result = data.slice(*@table.columns.input_keys) if @outs_functions end
Private Instance Methods
# File lib/csv_decision/result.rb, line 186 def add_cell(column_name:, cell:) case (current = @attributes[column_name]) when nil @attributes[column_name] = cell when Matchers::Proc @attributes[column_name] = [current, cell] @multi_result = true when Array @attributes[column_name] << cell else @attributes[column_name] = [current, cell] @multi_result = true end end
# File lib/csv_decision/result.rb, line 116 def check_if_column(col) delete_rows = [] @attributes[col].each_with_index { |value, index| delete_rows << index unless value } # Remove this if: column from the final result @attributes.delete(col) # Adjust the row index as we delete rows in sequence. delete_rows.each_with_index { |index, sequence| delete_row(index - sequence) } end
Each result “row”, given by the row index
is a collection of column arrays. @param index [Integer] Row index. @return [{Symbol=>Object}, {Integer=>Object}]
# File lib/csv_decision/result.rb, line 130 def delete_row(index) @attributes.transform_values { |value| value.delete_at(index) } end
# File lib/csv_decision/result.rb, line 165 def eval_out_proc(cell:, column_name:, column_type:) @attributes[column_name] = cell.function[@partial_result] # Do not add if: columns to the partial result return if column_type == :if @partial_result[column_name] = @attributes[column_name] end
# File lib/csv_decision/result.rb, line 146 def eval_outs_constants(row:) @outs.each_pair do |col, column| cell = row[col] next if cell.is_a?(Matchers::Proc) @partial_result[column.name] = cell @attributes[column.name] = cell end end
# File lib/csv_decision/result.rb, line 156 def eval_outs_procs(row:) @outs.each_pair do |col, column| cell = row[col] next unless cell.is_a?(Matchers::Proc) eval_out_proc(cell: cell, column_name: column.name, column_type: column.type) end end
# File lib/csv_decision/result.rb, line 110 def multi_row_result @table.columns.ifs.each_key { |col| check_if_column(col) } normalize_result end
@return [{Symbol=>Object}] Decision
result hash with any if: columns removed.
# File lib/csv_decision/result.rb, line 135 def normalize_result # Peek at the first column's result and see how many rows it contains. count = @attributes.values.first.count @multi_result = count > 1 return {} if count.zero? return @attributes.transform_values!(&:first) if count == 1 @attributes end
# File lib/csv_decision/result.rb, line 173 def partial_result(index) @attributes.each_pair do |column_name, values| value = values[index] # Delete this column from the partial result in case there is data from a prior result row next @partial_result.delete(column_name) if value.is_a?(Matchers::Proc) # Add this constant value to the partial result row built so far. @partial_result[column_name] = value end @partial_result end
Case where we have a single row result, which either gets returned or filtered by the if: column conditions.
# File lib/csv_decision/result.rb, line 99 def single_row_result # All if: columns must evaluate to true if @table.columns.ifs.keys.all? { |col| @attributes[col] } # Delete if: columns from final result @table.columns.ifs.each_key { |col| @attributes.delete(col) } return @attributes end false end