Helper Functions
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"
"×tamp": "@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.