Ivandt logo

Execution order

Understanding how multiple transformers are executed

How transformers execute

When multiple transformers are applied to a field, they execute sequentially in the order they appear in the transformers array. Each transformer receives the output of the previous transformer as its input.

This means the order matters significantly for the final result.

Basic example

{
  key: 'email',
  label: 'Email',
  type: 'text',
  transformers: [
    {
      action: 'trimWhitespace'  // Runs first
    },
    {
      action: 'toLowerCase'      // Runs second
    }
  ]
}

Execution flow:

  1. Input: " JOHN@EXAMPLE.COM "
  2. After trimWhitespace: "JOHN@EXAMPLE.COM"
  3. After toLowerCase: "john@example.com"
  4. Final output: "john@example.com"

Order matters

Changing the order of transformers can produce different results:

Example 1: Trim then uppercase

{
  key: 'code',
  label: 'Code',
  type: 'text',
  transformers: [
    {
      action: 'trimWhitespace'
    },
    {
      action: 'toUpperCase'
    }
  ]
}
  • Input: " abc123 "
  • After trim: "abc123"
  • After uppercase: "ABC123"
  • Final: "ABC123"

Example 2: Uppercase then trim

{
  key: 'code',
  label: 'Code',
  type: 'text',
  transformers: [
    {
      action: 'toUpperCase'
    },
    {
      action: 'trimWhitespace'
    }
  ]
}
  • Input: " abc123 "
  • After uppercase: " ABC123 " (spaces preserved)
  • After trim: "ABC123"
  • Final: "ABC123"

In this case, both produce the same result, but the intermediate values differ.

Complex example with multiple transformers

{
  key: 'price',
  label: 'Price',
  type: 'numeric',
  transformers: [
    {
      action: 'trimWhitespace'      // 1. Remove spaces
    },
    {
      action: 'cleanNumbers',        // 2. Parse and clean
      payload: {
        dropNonNumbers: true,
        options: {
          allowCurrency: true,
          decimalHandling: 'round',
          decimalPlaces: 2
        }
      }
    },
    {
      action: 'clampRange',          // 3. Limit to range
      payload: {
        min: 0,
        max: 10000
      }
    }
  ]
}

Execution flow:

  1. Input: " $12,345.678 "
  2. After trimWhitespace: "$12,345.678"
  3. After cleanNumbers: 12345.68 (parsed, rounded to 2 decimals)
  4. After clampRange: 10000 (clamped to max)
  5. Final output: 10000

Best practices

1. Clean data first

Always start with data cleaning transformers:

transformers: [
  { action: 'trimWhitespace' },      // Clean first
  { action: 'toLowerCase' },          // Then format
  { action: 'formatEmails' }          // Then validate/normalize
]

2. Parse before manipulating

For numeric data, parse before applying numeric operations:

transformers: [
  { 
    action: 'cleanNumbers',           // Parse first
    payload: { dropNonNumbers: true }
  },
  { 
    action: 'roundNumbers',           // Then manipulate
    payload: { decimals: 2 }
  },
  {
    action: 'scaleNumbers',           // Then scale
    payload: { factor: 100 }
  }
]

3. Combine columns last

When using combineColumns, place it after other transformers on the source fields:

// First name field
{
  key: 'firstName',
  label: 'First name',
  type: 'text',
  transformers: [
    { action: 'trimWhitespace' },
    { action: 'titleCaseText', payload: { allWords: true } }
  ]
}

// Last name field
{
  key: 'lastName',
  label: 'Last name',
  type: 'text',
  transformers: [
    { action: 'trimWhitespace' },
    { action: 'titleCaseText', payload: { allWords: true } }
  ]
}

// Combined field - runs after source fields are cleaned
{
  key: 'fullName',
  label: 'Full name',
  type: 'text',
  transformers: [
    {
      action: 'combineColumns',
      payload: {
        template: '{firstName} {lastName}'
      }
    }
  ]
}

4. Default values early or late

Depending on your use case:

Early (before other transformers):

transformers: [
  { 
    action: 'defaultValue',           // Fill empty first
    payload: { defaultValue: '0' }
  },
  { 
    action: 'cleanNumbers',           // Then parse
    payload: { dropNonNumbers: true }
  }
]

Late (after other transformers):

transformers: [
  { 
    action: 'cleanNumbers',           // Try to parse first
    payload: { dropNonNumbers: true }
  },
  { 
    action: 'defaultValue',           // Fill remaining empty
    payload: { defaultValue: 0 }
  }
]

When transformers run

Transformers can be configured to run at different times:

  • On load: When data is first imported
  • On cell change: When a user edits a cell
  • On error: After validation errors are detected

By default, transformers run on both load and cell change. The execution order within each trigger remains the same as defined in the array.

Performance considerations

Each transformer processes all rows in the column. For large datasets:

  1. Minimize transformer count: Only use necessary transformers
  2. Order efficiently: Place transformers that might skip processing early (e.g., type checks)
  3. Combine when possible: Use regexReplace instead of multiple string replacements

Debugging transformer chains

To debug transformer execution:

  1. Test incrementally: Add transformers one at a time
  2. Check intermediate values: Use console logging in custom transformers
  3. Verify order: Ensure transformers are in the correct sequence
  4. Test edge cases: Empty values, null, special characters

Common patterns

Email normalization

transformers: [
  { action: 'trimWhitespace' },
  { action: 'toLowerCase' },
  { action: 'formatEmails' }
]

Phone number formatting

transformers: [
  { action: 'trimWhitespace' },
  { 
    action: 'formatPhoneNumbers',
    payload: {
      countries: ['US', 'CA'],
      withInternationalCode: true
    }
  }
]

Currency to number

transformers: [
  { action: 'trimWhitespace' },
  { 
    action: 'cleanNumbers',
    payload: {
      dropNonNumbers: true,
      options: {
        allowCurrency: true,
        decimalHandling: 'round',
        decimalPlaces: 2
      }
    }
  }
]

Name formatting

transformers: [
  { action: 'trimWhitespace' },
  { action: 'titleCaseText', payload: { allWords: true } }
]