FormulaCondition

Pairs the variables of an analysis formula with their condition fragments and builds the Excel predicates from them. LstVars takes the variable list and LstConditions the matching condition list. Valid says whether the two lists line up and whether every variable sits in one table, VariablesTable names that table, ConditionString renders one condition wrapped in an IF, and ConditionPredicate joins the conditions into a single predicate. Variables and Conditions give the two lists back, and entries filed on the way leave through HasCheckings and CheckingValues.

Depends on: BetterArray, Checking, LLVariables, LLdictionary

Version: 1.0 (2026-02-09)

Factory

Create #

create

Construct a FormulaCondition instance from paired arrays

Signature:

Public Function Create(ByVal formVar As BetterArray, ByVal formCond As BetterArray) As FormulaCondition

Validate paired arrays and expose the ready-to-use condition helper.

Validates that both arrays are non-Nothing and have equal lengths, then stores normalised copies with a lower bound of 1.

Parameters:

  • formVar: BetterArray. The array containing variable names.
  • formCond: BetterArray. The array containing predicate fragments.

Returns: FormulaCondition. A ready-to-use condition helper.

Throws:

  • ProjectError.InvalidArgument When formVar or formCond is Nothing or lengths differ.

PublicAccessors

LstConditions #

conditions

Retrieve the stored condition fragments

Signature:

Public Property Get LstConditions() As BetterArray

Provide controlled access to stored collections.

Returns the internal BetterArray holding the condition predicates in the same order they were supplied at creation.

Returns: BetterArray. The condition fragments preserving the original order.


LstVars #

variables

Retrieve the stored variables list

Signature:

Public Property Get LstVars() As BetterArray

Returns the internal BetterArray holding the variable names in the same order they were supplied at creation.

Returns: BetterArray. The variable names preserving the original order.


InternalState

HasCheckings #

has-checkings

Determine whether diagnostics have been captured

Signature:

Public Property Get HasCheckings() As Boolean

Returns True when at least one LogInfo call has been made since the last validation run, indicating that diagnostic information is available.

Returns: Boolean. True when internal checks exist.


MetadataHelpers

VariablesTable #

variables-table

Resolve the table name shared by all tracked variables

Signature:

Public Property Get VariablesTable(ByVal dict As LLdictionary) As String

Provide metadata derived from the last validation run.

Returns the cached table name when validation has already occurred. When the cache is empty, triggers a full validation pass against the supplied dictionary before returning the result. Formulas.ParsedCustomFormula calls this as a fallback when the table name is unknown, so it sits on the hot path for N and N() formulas.

Parameters:

  • dict: LLdictionary. The dictionary used when validation has not occurred yet.

Returns: String. Table identifier or empty string when unavailable.


Validation

Valid #

valid

Validate variable/condition lists against the dictionary

Signature:

Public Function Valid(ByVal dict As LLdictionary, Optional ByVal tablename As String = vbNullString) As Boolean

Confirm that variables/conditions are aligned and belong to the same table.

Checks that the lists are non-empty, then verifies each variable exists in the dictionary and belongs to the expected table. When a tablename argument is supplied it overrides the table resolved from the first variable. The result is cached against BOTH the tablename argument and the dictionary object. Keying on the tablename alone handed the answer computed for one dictionary back to a caller asking about another one.

Parameters:

  • dict: LLdictionary. The metadata provider used for variable lookups.
  • tablename: Optional String. When supplied, forces the expected table name. Defaults to vbNullString.

Returns: Boolean. True when validation succeeds.


AdoptVariablesHelper #

adopt-variables-helper

Take the helper the owner already holds for this dictionary

Signature:

Public Sub AdoptVariablesHelper(ByVal varsHelper As LLVariables, ByVal dict As LLdictionary)

A fresh instance of this class is built for every analysis formula, and each one used to read the dictionary sheet again to build its own LLVariables: several block reads per formula, repeated for every column of every table. Formulas holds one helper per table and hands it in here before Valid runs, so VariablesProvider finds the pair already bound. The helper handed in must have been built over dict; the caller owns that pairing.

Parameters:

  • varsHelper: LLVariables. The helper to reuse.
  • dict: LLdictionary. The dictionary the helper was built over.

FormulaRendering

ConditionString #

condition-string

Wrap the predicate in an IF expression referencing the supplied variable

Signature:

Public Function ConditionString(ByVal tablename As String, ByVal varName As String, _
                                Optional ByVal Connector As String = "*") As String

Produce Excel-ready predicates and wrapper formulas.

Joins all variable/condition pairs using ConditionPredicate, then wraps the result in an IF function that returns the variable value when the predicate evaluates to True. Returns an empty string when the predicate is empty. The space before the comma is deliberate: CrossTableFormula compares generated formulas in its tests.

Parameters:

  • tablename: String. Structured reference table name.
  • varName: String. Variable/column name to return when the condition is met.
  • Connector: Optional String. Connector used between predicates. Defaults to "*".

Returns: String. An IF expression or empty string when the predicate is empty.


ConditionPredicate #

condition-predicate

Join the variable predicates using the supplied connector

Signature:

Public Function ConditionPredicate(ByVal tablename As String, Optional ByVal Connector As String = "*") As String

Iterates through all variable/condition pairs and concatenates them into a single predicate string using the supplied connector (typically "*" for AND logic). Each fragment is wrapped in parentheses using structured table references, and the condition fragment carries its own operator: ">= 5" becomes (f_table[age]>= 5). Formulas.ClearCountIf rewrites those fragments for COUNTIFS, so the two have to keep agreeing on the shape. The answer is held against the table name and the connector. The lists cannot change after Seal, so the same pair always gives the same string, and Formulas.ParsedAnalysisFormula asks for it once per variable token of the same formula.

Parameters:

  • tablename: String. Structured reference table name.
  • Connector: Optional String. Connector between predicate fragments. Defaults to "*".

Returns: String. Concatenated predicate or empty string when inputs are missing.


PublicDiagnostics

Variables #

variables-clone

Retrieve a clone of the stored variable names

Signature:

Public Property Get Variables() As BetterArray

Defensive copies of the tracked lists and the diagnostic log.

Returns a BetterArray copy of the variable identifiers in the order they were supplied at creation.

Returns: BetterArray. The variable names preserving the original order.


Conditions #

conditions-clone

Retrieve a clone of the stored condition fragments

Signature:

Public Property Get Conditions() As BetterArray

Returns a BetterArray copy of the predicate fragments in the order they were supplied at creation.

Returns: BetterArray. The condition fragments preserving the original order.


CheckingValues #

checking-values

Retrieve the internal checking log

Signature:

Public Property Get CheckingValues() As Checking

Returns the accumulated Checking instance containing all diagnostic messages from the last validation run, or Nothing when none recorded.

Returns: Checking. The diagnostic log.


Internal members (not exported)

Factory

Seal #

seal

Prevent further changes to setup-only properties

Signature:

Public Sub Seal()

Marks the instance as sealed so guarded setters raise when invoked after construction. Called by the factory before returning.


GuardNotSealed #

guard-not-sealed

Reject writes to setup-only properties after sealing

Signature:

Private Sub GuardNotSealed(ByVal propName As String)

Raises an error when a guarded setter is invoked on a sealed instance.

Parameters:


PublicAccessors

LstConditions #

conditions-set

Store the condition fragments and reset caches

Signature:

Public Property Set LstConditions(ByVal formCond As BetterArray)

Copies the incoming array into a new BetterArray normalised to a lower bound of 1, then resets all cached validation artefacts so the next call to Valid triggers a fresh check. Both setters copy with LowerBound 1, so the two lists always share one lower bound. ConditionPredicate indexes both lists from that bound and it is correct because of this.

Parameters:


LstVars #

variables-set

Store the variables list, normalise bounds, and reset caches

Signature:

Public Property Set LstVars(ByVal formVar As BetterArray)

Copies the incoming array into a new BetterArray normalised to a lower bound of 1, then invalidates all cached data.

Parameters:


InternalState

ResetCaches #

reset-caches

Clear cached table names, validation results, and diagnostics

Signature:

Private Sub ResetCaches()

Maintain cached validation artefacts and diagnostics.

Resets all memoised state so the next call to Valid, VariablesTable or ConditionPredicate starts fresh. Its two callers are the setters above, and both are guarded by Seal, so this runs at construction only.


ClearCheckings #

clear-checkings

Drop the diagnostic entries of the previous validation run

Signature:

Private Sub ClearCheckings()

LogInfo #

log-info

Append a diagnostic entry to the internal checking log

Signature:

Private Sub LogInfo(ByVal message As String, Optional ByVal scope As Byte = checkingInfo)

Creates the checking instance on first use and appends the message using a monotonically increasing counter key. The log is what says WHY a formula failed: the analysis path gives up quietly when Valid is False, and the reason lives here.

Parameters:


Validation

VariablesProvider #

variables-provider

Build the variables helper once per dictionary

Signature:

Private Function VariablesProvider(ByVal dict As LLdictionary) As LLVariables

Valid used to build a fresh LLVariables on every call, and Formulas.ParsedAnalysisFormula calls Valid once per variable token of every analysis formula. Each helper carries its own block reads over the same dictionary worksheet. The helper is held against the dictionary it was built over, so a call naming another dictionary builds a new one.

Parameters:

Returns: LLVariables. Helper bound to that dictionary.


ErrorHandling

ThrowError #

throw-error

Raise a VBA error tagged with the class name

Signature:

Private Sub ThrowError(ByVal errNumb As Long, ByVal errorMessage As String)

Centralised helper to raise ProjectError-compliant errors.

Wrapper around Err.Raise that standardises the source to "FormulaCondition", providing a consistent stack trace.

Parameters:

Throws:


Used in (6 file(s))