Idiomatic Patterns

Common patterns for solving typical Jyro tasks.

Input validation with guard clauses

Validate inputs early and exit on failure:

if Data.items is null or Data.items is not array then
    fail "Items must be an array"
end

if Length(Data.items) == 0 then
    Data.result = []
    exit "No items to process"
end

if Data.email is null or Data.email == "" then
    fail "Email is required"
end

# Proceed with valid data

Safe property access

Use short-circuit evaluation to prevent null reference errors:

if Data.user is not null and Data.user.profile is not null then
    Data.userName = Data.user.profile.name
else
    Data.userName = "Guest"
end

# Null coalescing for simple defaults
var name = Data.user.name ?? "Unknown"

Array processing pipeline

Chain query and array functions to build a filter-sort-extract pipeline:

var active = WhereByField(Data.employees, "active", "==", true)
var engineering = WhereByField(active, "department", "==", "Engineering")
var sorted = SortByField(engineering, "salary", "desc")
var names = Select(sorted, "name")
Data.topEngineers = names

Aggregation with select

Extract field values first, then aggregate:

var prices = Select(Data.items, "price")
Data.total = Sum(prices)
Data.average = Average(prices)
Data.highest = Max(prices)
Data.lowest = Min(prices)

Configuration defaults with merge

Use Merge to apply user overrides on top of defaults:

Data.config = Merge([
    {"timeout": 30, "retries": 3, "debug": false},   # Defaults
    Data.config                                        # User overrides
])

Data enrichment

Add computed properties to the Data context:

Data.count = Length(Data.items)
Data.total = Sum(Select(Data.items, "price"))
Data.hasItems = Length(Data.items) > 0
Data.uniqueCategories = Distinct(Select(Data.items, "category"))
exit

Chained transforms

Pipe results through a sequence of functions:

var names = Select(Data.users, "name")
var upper = Map(names, n => ToUpper(n))
var sorted = Sort(upper)
Data.result = Join(sorted, ", ")

Accumulation with loops

When lambdas and query functions are insufficient for complex per-item logic, use a manual loop:

var total = 0
foreach item in Data.items do
    if item.price is number and item.taxable then
        total += item.price * 1.1
    elseif item.price is number then
        total += item.price
    end
end
Data.totalWithTax = total

Collecting nested arrays

Flatten nested array fields and deduplicate:

var allTags = SelectMany(Data.documents, "tags")
Data.uniqueTags = Distinct(allTags)

API response shaping

Use Project and Omit to control which fields appear in output:

var activeUsers = WhereByField(Data.users, "active", "==", true)
Data.response = Project(activeUsers, ["id", "name", "email"])
Data.sanitized = Omit(Data.users, ["password", "token", "secret"])

Scrubbing sensitive fields from Data

Use delete to remove properties directly from Data. This is the only way to completely remove keys from the output, since Data cannot be reassigned:

# Remove sensitive fields before returning to host
delete Data.password
delete Data.token
delete Data.user.secret

# Dynamic scrubbing
var sensitiveFields = ["ssn", "creditCard", "pin"]
foreach field in sensitiveFields do
    delete Data[field]
end

Note: Omit works on arrays of objects and returns a new array. delete works directly on any single object, including Data itself.

Validation helpers with functions

Extract repetitive validation into pure functions. Each function validates one concern and either returns the value or terminates the script:

func RequireString(value, fieldName)
    if value is not string or value == "" then
        fail fieldName + " is required"
    end
    return value
end

func RequireNumber(value, fieldName, min, max)
    if value is not number then
        fail fieldName + " must be a number"
    end
    if value < min or value > max then
        fail fieldName + " must be between " + ToString(min) + " and " + ToString(max)
    end
    return value
end

var name = RequireString(Data.name, "name")
var age = RequireNumber(Data.age, "age", 0, 150)
Data.record = { "name": name, "age": age }

Composing functions with lambdas

Define complex logic in a function and reference it from a lambda. This keeps lambda expressions concise while the function handles multi-step logic:

func IsEligible(customer)
    if customer.age < 18 then return false end
    if customer.status != "active" then return false end
    if customer.balance < 0 then return false end
    return true
end

var eligible = Where(Data.customers, c => IsEligible(c))
Data.eligibleCount = Length(eligible)

Union-based result handling

Use a Result union to separate success from failure in a structured, compiler-checked way:

union Result
    Ok(value)
    Error(message: string)
end

func Divide(a: number, b: number)
    if b == 0 then
        return Error("Division by zero")
    end
    return Ok(a / b)
end

var result = Divide(Data.numerator, Data.denominator)
match result do
    case Ok(v) then
        Data.quotient = v
    case Error(msg) then
        Data.error = msg
end

The caller must handle both cases - match enforces it. Compare this with returning null on error, where the caller might forget to check.

“Private” members for metadata

Start identifier names with an underscore to differentiate metadata or data returned for further host processing from business data:

Data._debug = {...}
Data._processingStats = {...}
Data._payload = {...}

Back to top

Copyright © Mesch Systems 2025-2026. All rights reserved.