Helpers
DDO: Helper Functions
DDO provides a rich set of template helpers that can be used within template expressions to perform calculations, transformations, and data manipulation. Template helpers are functions that start with @ and can be used in various expression contexts.
Template Expression Syntax
DDO supports multiple ways to use template expressions:
# String interpolation (result is always a string)
{
    "message": "Hello {{ user.name }}, you have {{ @length(notifications) }} notifications"
}
# Native type preservation with ?{{ }}
{
    "count": "?{{ @length(notifications) }}",        # Returns number, not string
    "isActive": "?{{ user.status == 'active' }}",    # Returns boolean, not string
    "userData": "?{{ user }}"                         # Returns object, not string
}
# Shorthand syntax with @@ prefix
{
    "@@count": "@length(notifications)",               # Equivalent to ?{{ @length(notifications) }}
    "@@isActive": "user.status == 'active'",          # Equivalent to ?{{ user.status == 'active' }}
    "@@fullName": "user.firstName + ' ' + user.lastName"
}
# Mixed expressions
{
    "summary": "User {{ user.name }} has {{ @length(user.orders) }} orders",
    "@@totalValue": "@sum(@map(user.orders, 'total'))",
    "isVip": "?{{ @length(user.orders) >= 10 }}"
}
Working with Nested Data
Template helpers can access and manipulate nested data structures:
# Access nested properties
{
    "@@userAge": "user.profile.age",
    "@@firstOrderTotal": "user.orders[0].total",
    "@@adminEmails": "@map(@filter(users, 'role == \"admin\"'), 'email')"
}
# Complex nested operations
{
    "departmentStats": {
        "@@totalEmployees": "@length(departments[0].employees)",
        "@@avgSalary": "@avg(@map(departments[0].employees, 'salary'))",
        "@@seniorCount": "@length(@filter(departments[0].employees, 'level == \"senior\"'))"
    }
}
# JMESPath with templates
{
    "@@activeUserEmails": "@map(@search(users, '[?status==`active`]'), 'email')",
    "@@recentOrders": "@search(orders, '[?created_at>=`2024-01-01`]')"
}
Date and Time Helpers
@now()
@now(shift)
Get the current UTC datetime.
{
    "createdAt": "{{ @now() }}",           # String: "2024-01-15T10:30:00Z"
    "@@timestamp": "@now()",                # DateTime object
    "scheduledFor": "{{ @now('+2days') }}" # Shifted datetime
}
@format_date(date, format)
Format a date using a specified format string.
{
    "displayDate": "{{ @format_date(user.createdAt, 'YYYY-MM-DD') }}",
    "shortDate": "{{ @format_date(@now(), 'MMM DD') }}",
    "timestamp": "{{ @format_date(order.date, 'YYYY-MM-DD HH:mm:ss') }}"
}
# Format codes: YYYY (year), MM (month), DD (day), HH (hour), mm (minute), ss (second)
@timestamp()
Get the current Unix timestamp.
{
    "created": "{{ @timestamp() }}",       # String: "1705320600"
    "@@createdTs": "@timestamp()",          # Number: 1705320600
    "expiresAt": "?{{ @timestamp() + 3600 }}" # Number: timestamp + 1 hour
}
@time_ago(date)
Get human-readable relative time.
{
    "lastSeen": "{{ @time_ago(user.lastLogin) }}",    # "2 hours ago"
    "orderAge": "{{ @time_ago(order.createdAt) }}",   # "3 days ago"
    "accountAge": "{{ @time_ago(user.registeredAt) }}" # "2 months ago"
}
@duration(seconds)
Format a duration in seconds to human-readable form.
{
    "sessionLength": "{{ @duration(user.sessionTime) }}",    # "2 hours 30 minutes"
    "videoLength": "{{ @duration(video.durationSeconds) }}", # "1 hour 15 minutes"
    "@@totalTime": "@duration(@sum(@map(sessions, 'duration')))" # Duration object
}
ID Generation Helpers
@uuid()
Generate a UUID without dashes.
{
    "sessionId": "{{ @uuid() }}",          # String: "a1b2c3d4e5f6..."
    "@@userId": "@uuid()",                  # String: "a1b2c3d4e5f6..."
    "trackingCode": "TRK-{{ @uuid() }}"    # String: "TRK-a1b2c3d4e5f6..."
}
@uuid4()
Generate a standard UUID4 with dashes.
{
    "orderId": "{{ @uuid4() }}",           # String: "a1b2c3d4-e5f6-4789-..."
    "@@documentId": "@uuid4()",             # String: "a1b2c3d4-e5f6-4789-..."
}
@shortid()
Generate a short, URL-safe ID (8 characters).
{
    "code": "{{ @shortid() }}",            # String: "a1B2c3D4"
    "@@referralCode": "@shortid()",         # String: "a1B2c3D4"
    "inviteLink": "https://app.com/invite/{{ @shortid() }}"
}
@md5(value)
Generate MD5 hash of a value.
{
    "emailHash": "{{ @md5(user.email) }}",         # For Gravatar URLs
    "@@passwordHash": "@md5(user.password)",        # Hash for storage
    "cacheKey": "user_{{ @md5(user.id) }}"        # Cache key generation
}
String Manipulation Helpers
@uppercase(text)
@lowercase(text)
Convert text case.
{
    "displayName": "{{ @uppercase(user.firstName) }}",
    "email": "{{ @lowercase(user.email) }}",
    "@@code": "@uppercase(product.sku)",
    "urlSlug": "{{ @lowercase(@replace(title, ' ', '-')) }}"
}
@titlecase(text)
@capitalize(text)
Format text for display.
{
    "title": "{{ @titlecase(article.title) }}",       # "Hello World"
    "name": "{{ @capitalize(user.firstName) }}",      # "John"
    "@@displayTitle": "@titlecase(product.name)"
}
@trim(text)
Remove whitespace from text.
{
    "cleanEmail": "{{ @trim(user.email) }}",
    "@@username": "@trim(@lowercase(user.inputName))",
    "description": "{{ @trim(product.description) }}"
}
@length(value)
Get the length of strings, arrays, or objects.
{
    "nameLength": "{{ @length(user.name) }}",         # String length
    "@@itemCount": "@length(cart.items)",              # Array length
    "fieldCount": "?{{ @length(user.profile) }}",    # Object property count
    "hasItems": "?{{ @length(cart.items) > 0 }}"     # Boolean result
}
@slice(text, start, end)
Extract substring using slice notation.
{
    "initials": "{{ @slice(user.firstName, 0, 1) }}{{ @slice(user.lastName, 0, 1) }}",
    "preview": "{{ @slice(article.content, 0, 100) }}...",
    "@@productCode": "@slice(product.sku, 0, 6)"
}
@replace(text, old, new)
Replace text within strings.
{
    "phoneDisplay": "{{ @replace(user.phone, '+1', '') }}",
    "urlSlug": "{{ @replace(@lowercase(title), ' ', '-') }}",
    "@@cleanDescription": "@replace(product.description, '\\n', ' ')"
}
@split(text, separator)
Split string into array.
{
    "tags": "?{{ @split(product.tagString, ',') }}",      # Returns array
    "@@skills": "@split(user.skillsText, ';')",           # Array of skills
    "nameParts": "?{{ @split(user.fullName, ' ') }}"     # ["John", "Doe"]
}
@slugify(text)
Convert text to URL-friendly slug.
{
    "articleSlug": "{{ @slugify(article.title) }}",      # "hello-world-article"
    "@@productUrl": "@slugify(product.name)",             # "awesome-product"
    "categoryPath": "/{{ @slugify(category.name) }}/{{ @slugify(product.name) }}"
}
Mathematical Helpers
Basic Math Operations
Perform mathematical calculations.
{
    "total": "?{{ @add(order.subtotal, order.tax) }}",
    "discount": "?{{ @mul(order.subtotal, 0.1) }}",      # 10% discount
    "@@finalPrice": "@sub(product.price, product.discount)",
    "average": "?{{ @div(@sum(scores), @length(scores)) }}"
}
@round(number)
@floor(number)
@ceil(number)
Round numbers to integers.
{
    "rating": "?{{ @round(product.averageRating) }}",    # 4
    "pages": "?{{ @ceil(@div(content.length, 500)) }}",  # Round up
    "@@priceRange": "@floor(product.price / 100) * 100"   # Price to nearest 100
}
@random(min, max)
Generate random numbers.
{
    "orderNumber": "ORD-{{ @random(10000, 99999) }}",
    "@@priority": "@random(1, 10)",                       # Random priority
    "couponCode": "SAVE{{ @random(10, 99) }}"
}
Collection Helpers
@sum(array)
@avg(array)
Calculate sum and average of arrays.
{
    "totalAmount": "?{{ @sum(@map(orders, 'total')) }}",
    "@@averageScore": "@avg(@map(reviews, 'rating'))",
    "cartTotal": "?{{ @sum(@map(cart.items, 'price')) }}"
}
@min(array)
@max(array)
Find minimum and maximum values.
{
    "lowestPrice": "?{{ @min(@map(products, 'price')) }}",
    "@@highestRating": "@max(@map(reviews, 'rating'))",
    "oldestUser": "?{{ @min(@map(users, 'createdAt')) }}"
}
@filter(array, expression)
Filter arrays based on conditions.
{
    "@@activeUsers": "@filter(users, 'status == \"active\"')",
    "highRatedProducts": "?{{ @filter(products, 'rating >= 4') }}",
    "@@recentOrders": "@filter(orders, 'createdAt >= \"2024-01-01\"')"
}
@map(array, expression)
Transform array elements.
{
    "@@userEmails": "@map(users, 'email')",
    "productNames": "?{{ @map(products, 'name') }}",
    "@@orderTotals": "@map(orders, 'total')",
    "fullNames": "?{{ @map(users, 'firstName + \" \" + lastName') }}"
}
@sort_by(array, key, reverse)
Sort arrays by a property.
{
    "@@sortedUsers": "@sort_by(users, 'createdAt', true)",    # Newest first
    "topProducts": "?{{ @sort_by(products, 'rating', true) }}",
    "@@orderedItems": "@sort_by(cart.items, 'price', false)"  # Cheapest first
}
@group_by(array, key)
Group array elements by a property.
{
    "@@usersByRole": "@group_by(users, 'role')",
    "ordersByStatus": "?{{ @group_by(orders, 'status') }}",
    "@@productsByCategory": "@group_by(products, 'category')"
}
@unique(array, key)
Get unique values from an array.
{
    "@@uniqueCategories": "@unique(@map(products, 'category'))",
    "userRoles": "?{{ @unique(@map(users, 'role')) }}",
    "@@activeCities": "@unique(@map(@filter(users, 'active'), 'city'))"
}
Conditional Helpers
@if_else(condition, trueValue, falseValue)
Conditional logic within templates.
{
    "status": "{{ @if_else(user.isActive, 'Online', 'Offline') }}",
    "@@canEdit": "@if_else(user.role == 'admin', true, false)",
    "displayPrice": "{{ @if_else(product.onSale, product.salePrice, product.price) }}"
}
@switch(value, cases, default)
Multi-condition switching.
{
    "statusColor": "{{ @switch(order.status, {'pending': 'yellow', 'shipped': 'blue', 'delivered': 'green'}, 'gray') }}",
    "@@priority": "@switch(user.role, {'admin': 10, 'moderator': 5, 'user': 1}, 0)",
    "icon": "{{ @switch(file.type, {'pdf': 'file-pdf', 'image': 'image', 'video': 'play'}, 'file') }}"
}
@coalesce(value1, value2, ...)
Return the first non-null/non-empty value.
{
    "displayName": "{{ @coalesce(user.nickname, user.firstName, 'Anonymous') }}",
    "@@userImage": "@coalesce(user.avatar, user.profilePicture, '/default-avatar.png')",
    "contactEmail": "{{ @coalesce(user.workEmail, user.personalEmail, 'no-email@example.com') }}"
}
Validation Helpers
@is_empty(value)
@is_null(value)
Check for empty or null values.
{
    "@@hasDescription": "?@is_empty(product.description)",
    "showProfile": "?{{ !@is_null(user.profile) }}",
    "@@requiresInput": "@is_empty(form.requiredField)"
}
@is_email(value)
@is_url(value)
Validate data formats.
{
    "@@validEmail": "@is_email(user.email)",
    "hasWebsite": "?{{ @is_url(company.website) }}",
    "@@contactValid": "@is_email(contact.email)"
}
Advanced Template Examples
Complex User Profile
{
    "userProfile": {
        "@@id": "@uuid4()",
        "@@fullName": "user.firstName + ' ' + user.lastName",
        "@@displayName": "@coalesce(user.nickname, user.firstName)",
        "@@email": "@lowercase(@trim(user.email))",
        "@@isActive": "user.status == 'active'",
        "@@memberSince": "@format_date(user.createdAt, 'YYYY-MM-DD')",
        "stats": {
            "@@orderCount": "@length(user.orders)",
            "@@totalSpent": "@sum(@map(user.orders, 'total'))",
            "@@averageOrder": "@div(@sum(@map(user.orders, 'total')), @length(user.orders))",
            "@@isVip": "@length(user.orders) >= 10"
        }
    }
}
Dynamic Product Catalog
{
    "catalog": {
        "@@totalProducts": "@length(products)",
        "@@categories": "@unique(@map(products, 'category'))",
        "priceRange": {
            "@@min": "@min(@map(products, 'price'))",
            "@@max": "@max(@map(products, 'price'))",
            "@@average": "@avg(@map(products, 'price'))"
        },
        "@@featuredProducts": "@filter(products, 'featured == true')",
        "@@topRated": "@sort_by(@filter(products, 'rating >= 4.5'), 'rating', true)"
    }
}
Order Summary with Calculations
{
    "orderSummary": {
        "orderId": "ORD-{{ @shortid() }}",
        "itemCount": "?{{ @length(cart.items) }}",
        "subtotal": "?{{ @sum(@map(cart.items, 'price * quantity')) }}",
        "@@tax": "@mul(@sum(@map(cart.items, 'price * quantity')), 0.08)",
        "shipping": "?{{ @if_else(@sum(@map(cart.items, 'price * quantity')) > 100, 0, 9.99) }}",
        "@@total": "@add(@add(@sum(@map(cart.items, 'price * quantity')), @mul(@sum(@map(cart.items, 'price * quantity')), 0.08)), @if_else(@sum(@map(cart.items, 'price * quantity')) > 100, 0, 9.99))",
        "estimatedDelivery": "{{ @format_date(@now('+3days'), 'YYYY-MM-DD') }}"
    }
}
Template helpers provide powerful data transformation capabilities that make DDO suitable for complex data processing scenarios while maintaining readability and ease of use.