Skip to content
synthreo.ai

Set Transformation - Synthreo Builder

Set Transformation node for Builder - reshape, filter, join, group, and remap datasets flowing between nodes using operations like inner-join, group-by, filter-by-js, and foreach-js.


The Set Transformation node reshapes data flowing through your workflow - cleaning, filtering, joining, grouping, flattening nested objects, and running custom logic - so downstream nodes receive exactly the structure they expect.

This is one of the most versatile nodes in Synthreo Builder. It handles a wide range of data shaping tasks that would otherwise require custom code, making it central to any workflow that works with structured datasets from APIs, databases, or prior processing nodes.


  • Items (Array of Objects, Required) - Records from previous nodes to transform.
  • (Optional) Secondary Items (Array of Objects) - A second dataset used by join and stack operations.

  • Transformed Items (Array of Objects) - Records after the selected transformation has been applied. The exact structure of each output record depends on the chosen operation and its settings.

NameTypeRequiredDefaultDescription
TransformationDropdownNononeThe operation to apply to the incoming dataset. Options are described in the Operations Reference section below.
Specific PropertiesString (CSV)No(empty)For replace-in-elements-a-by-b: limits replacements to these named properties only. Comma-separated list.
Wrapping PropertyStringNo(empty)For replace-in-elements-a-by-b: the property in each record that contains the replacement map or object.
Join AStringNo(empty)For inner-join-a-on-b: the key field name in dataset A to join on.
On B (Join)StringNo(empty)For inner-join-a-on-b: the key field name in dataset B to match against.
On property names (comma separated)String (CSV)No(empty)For group-by: one or more property names to use as grouping keys.
Column name for ‘count’StringNo(empty)For group-by: the name of the output column that will contain the count of records in each group.
Group by methodDropdownNoList of groupsFor group-by: controls what is returned. List of groups returns all groups with their items and counts. Keep top one item (cut the rest) returns only the first item from each group.
Filter ExpressionStringNo(empty)For filter-by: a condition expression such as status == "active" && total > 100. Records where the expression evaluates as true are kept; others are discarded.
Value Getter Expression (Array-to-string)StringNo(empty)For join-array-to-string: an expression that selects the value from each array element to include in the joined string.
SeparatorStringNo(empty)For join-array-to-string: the separator inserted between joined values, such as ", " or " | ".
Output property nameStringNojoined_stringFor join-array-to-string: the name of the output property that stores the resulting string.
Value Getter Expression (Redirect-to)StringNo(empty)For redirect-to: the path to nested data that should be lifted to the top-level output stream.
jsCodeCode (JavaScript)No(empty)For foreach-js: a JavaScript function in the form (item, idx) => item that receives each record and returns a modified version.
jsFilterCode (JavaScript)No(empty)For filter-by-js: a JavaScript predicate in the form (item, idx) => boolean that returns true to keep a record and false to discard it.

Parameters appear contextually in the configuration panel based on the selected Transformation. Only the fields relevant to the chosen operation are shown.


Passes data through without modification. Use as a placeholder or when no transformation is needed at this point in the workflow.

Replaces values in specified fields of each record using a replacement map stored in a property of that same record. Controlled by Specific Properties (which fields to replace) and Wrapping Property (where the replacement map lives).

Use case: Normalize status codes or label values across records that each carry their own mapping object.

Example: Standardize status values where each record has a status field and a standardized_values map object. Records with status = "ACTIVE" or status = "active" are both remapped to "Active".

Joins dataset A (primary input) and dataset B (secondary input) on matching key values. Records from A that have no matching key in B are excluded from the output. The matching fields are defined by Join A and On B (Join).

Use case: Enrich customer records from dataset A with subscription tier data from dataset B, matching on customer ID.

Example:

  • Dataset A: [{id: 1, name: "Alice"}]
  • Dataset B: [{user_id: 1, tier: "pro"}]
  • Join A: id, On B (Join): user_id
  • Output: [{id: 1, name: "Alice", tier: "pro"}]

Groups records by the values of one or more properties. Produces grouped output that includes the grouped items and a count per group. Controlled by On property names (comma separated), Column name for ‘count’, and Group by method.

Group by method options:

  • List of groups - Returns all groups, each containing their member records and the group count.
  • Keep top one item (cut the rest) - Returns only the first record from each group along with the count. Useful for deduplication or summarization.

Use case: Segment customers by purchase frequency and spending tier.

Keeps only records where the Filter Expression evaluates as true. Discards records that do not match. Supports boolean operators (&&, ||) and comparison operators (==, !=, >, <, >=, <=).

Use case: Keep only active records before sending data to a downstream API.

Example: status == "active" && total > 100 keeps records that are active and have a total above 100.

Takes an array property within each record, extracts a value from each element using Value Getter Expression (Array-to-string), and joins those values into a single string separated by Separator. The result is stored in Output property name.

Use case: Produce a human-readable product list from an array of ordered items.

Example: Items array [{name: "Laptop"}, {name: "Mouse"}] with Value Getter name and Separator ", " produces "Laptop, Mouse".

Zips or stacks properties from two input datasets into paired records. Useful for aligning parallel arrays or datasets that share a positional relationship but do not have a common key to join on.

Use case: Pair time-series labels from one dataset with corresponding measurements from another.

Lifts nested data from a specified path to the top-level output stream, effectively flattening nested objects. Controlled by Value Getter Expression (Redirect-to).

Use case: When an API response wraps all useful data inside a nested property, use redirect-to to move that nested data to the top level so downstream nodes can access it directly.

Example: Input [{order: {customer: {id: 1}}}] with expression order.customer produces [{id: 1}].

Runs a custom JavaScript function against each record in the dataset. The function receives (item, idx) and must return a modified version of the item. Use this for transformations that cannot be expressed with the other operations.

Function signature: (item, idx) => ({ ...item, ...changes })

Use case: Add a computed field to each record, such as calculating a discount amount from price and percentage fields.

Runs a custom JavaScript predicate against each record to decide whether to keep it. Returns true to include the record, false to discard it.

Function signature: (item, idx) => boolean

Use case: Apply multi-condition business logic for filtering that is too complex to express in the filter-by expression syntax.


A marketing workflow segments a customer list into cohorts based on purchase behavior for targeted campaign sending.

Configuration:

  • Transformation: group-by
  • On property names (comma separated): purchase_frequency,total_spent
  • Column name for 'count': customer_count
  • Group by method: List of groups

Result: Grouped cohorts with member records and counts per segment, ready for downstream campaign dispatch.

An integration receives records from two different source systems that use different values for the same status field. The transformation normalizes all values to a consistent set.

Configuration:

  • Transformation: replace-in-elements-a-by-b
  • Specific Properties: status,priority
  • Wrapping Property: standardized_values

Result: All status and priority values are normalized to the standard set, regardless of the source system’s original formatting.

An order record contains a line items array. The workflow needs a single comma-separated string of product names for use in an email notification.

Configuration:

  • Transformation: join-array-to-string
  • Value Getter Expression (Array-to-string): product_name
  • Separator: ", "
  • Output property name: ordered_products

Result: "Laptop, Mouse, Keyboard" stored in ordered_products on each order record.

Before sending customer records to an external CRM API, the workflow removes inactive and suspended accounts.

Configuration:

  • Transformation: filter-by
  • Filter Expression: status == "active"

Result: Only records with status = "active" are passed to the downstream API node.


  • Validate join keys: For inner-join-a-on-b, confirm that both sides use the same data type and formatting for the key fields (e.g., both are integers, or both are trimmed lowercase strings). Type mismatches or whitespace differences cause records to be excluded silently.
  • Filter early: When working with large datasets, place a filter-by or filter-by-js operation before expensive operations like joins and group-bys. This reduces the number of records processed and improves performance.
  • Name outputs descriptively: Use meaningful names for group count columns, joined string properties, and redirect-to destinations. Names like ordered_products, customer_count, and active_status make downstream configuration and debugging significantly easier than generic names.
  • Test with small samples: Run with a representative small dataset before processing large batches. This is especially important for foreach-js and filter-by-js where custom code may have edge cases.
  • Null-check in JavaScript operations: When writing foreach-js or filter-by-js code, always check for null or undefined values on nested fields before accessing them. Unguarded access on null values causes runtime errors that stop the workflow.

The inner-join-a-on-b operation only includes records where both datasets have a matching key. If some records from dataset A are missing in the output, their join key value did not match any value in dataset B. Check for type mismatches (number vs. string), extra whitespace, or case differences in the key fields.

Group-by returns only one record per group

Section titled “Group-by returns only one record per group”

This is expected behavior when Group by method is set to Keep top one item (cut the rest). Change the method to List of groups if you need all records within each group.

Verify that the Filter Expression syntax is correct and that the field names and values match the actual data. Field names are case-sensitive. String comparisons require quoted values (e.g., status == "active" not status == active).

foreach-js or filter-by-js throws a runtime error

Section titled “foreach-js or filter-by-js throws a runtime error”

Check the JavaScript code for null-unsafe field access. When a record has a missing or null field and the code tries to access a property on it, a runtime error occurs. Add null checks (e.g., item.address && item.address.city) to protect against missing values.

Confirm that the Value Getter Expression (Redirect-to) path correctly matches the nested structure of the input data. If the path does not exist in any record, the output will be empty. Inspect the input record structure to verify the exact property path.


  • Join happy path: Dataset A [{id: 1, name: "A"}], Dataset B [{user_id: 1, tier: "pro"}], Join A = id, On B (Join) = user_id - Expected: [{id: 1, name: "A", tier: "pro"}]
  • Group with counts: [{cat: "A"}, {cat: "A"}, {cat: "B"}], On property names = cat, Column name for 'count' = count - Expected: Group A with count 2, Group B with count 1
  • Filter expression: [{status: "active"}, {status: "inactive"}], Filter Expression = status == "active" - Expected: [{status: "active"}]
  • Join array to string: Items [{name: "A"}, {name: "B"}], Value Getter = name, Separator ", " - Expected: ordered_products = "A, B"
  • Redirect-to: [{order: {customer: {id: 1}}}], Value Getter = order.customer - Expected: [{id: 1}]

  • ConvertToJSON - Converts the transformed output of this node into JSON format for API submission.
  • String Operation - For single-field text manipulation after data reshaping with this node.
  • Custom Script - For highly custom transformation logic that exceeds the capabilities of foreach-js within this node.
  • Filter By - A dedicated filtering node for simpler filter-only use cases.