Map Rendering Guide
View SourceMaster advanced data transformations with map directives.
Overview
The render_map function enables powerful data transformations within nested map structures using special directives. Directives allow you to iterate, filter, merge, and conditionally render maps—similar to a data transformation pipeline.
When to Use render_map
Use render_map when you need to:
- Transform nested data structures
- Build complex outputs programmatically
- Filter and map collections
- Merge multiple data sources
- Apply conditional logic to structured data
Use regular render when you need to:
- Generate text/HTML output
- Render simple templates
- Control flow in text documents
Basic Usage
input = %{
"greeting" => "Hello {{ name }}!"
}
context = %{"name" => "World"}
{:ok, result} = Mau.render_map(input, context)
# result: %{"greeting" => "Hello World!"}Directives Overview
All directives use keys starting with #:
| Directive | Purpose | Syntax |
|---|---|---|
#map | Iterate over collections | "#map" => [collection, template] |
#filter | Filter collections | "#filter" => [collection, condition] |
#merge | Combine maps | "#merge" => [map1, map2, ...] |
#if | Conditional rendering | "#if" => [condition, true_template, false_template] |
#pick | Extract keys from map | "#pick" => [map, keys] |
#pipe | Thread data through transformations | "#pipe" => [initial, directives] |
#map Directive
Iterate over a collection and apply a template to each item.
Syntax
%{
"key" => %{
"#map" => [collection_template, item_template]
}
}Basic Example
input = %{
"users" => %{
"#map" => [
"{{$users}}",
%{"name" => "{{$loop.item.name}}"}
]
}
}
context = %{
"$users" => [
%{"name" => "Alice"},
%{"name" => "Bob"}
]
}
{:ok, result} = Mau.render_map(input, context)
# result: %{"users" => [%{"name" => "Alice"}, %{"name" => "Bob"}]}Access Loop Variables
Available in the item template:
%{
"#map" => [
"{{$items}}",
%{
"index" => "{{$loop.index}}",
"item" => "{{$loop.item}}",
"is_first" => "{{$loop.first}}"
}
]
}Loop variables:
$loop.item- Current item$loop.index- Position (0-based)$loop.parentloop- Parent loop (in nested maps)
Nested Maps
input = %{
"departments" => %{
"#map" => [
"{{$departments}}",
%{
"name" => "{{$loop.item.name}}",
"employees" => %{
"#map" => [
"{{$loop.item.employees}}",
%{"name" => "{{$loop.item.name}}"}
]
}
}
]
}
}#filter Directive
Filter items based on a condition.
Syntax
%{
"key" => %{
"#filter" => [collection_template, condition_template]
}
}Example
input = %{
"active_users" => %{
"#filter" => [
"{{$users}}",
"{{$loop.item.active}}"
]
}
}
context = %{
"$users" => [
%{"name" => "Alice", "active" => true},
%{"name" => "Bob", "active" => false},
%{"name" => "Charlie", "active" => true}
]
}
{:ok, result} = Mau.render_map(input, context)
# result: %{"active_users" => [
# %{"name" => "Alice", "active" => true},
# %{"name" => "Charlie", "active" => true}
# ]}Complex Conditions
"#filter" => [
"{{$users}}",
"{{$loop.item.age > 18 and $loop.item.verified}}"
]#merge Directive
Combine multiple maps together.
Syntax
%{
"key" => %{
"#merge" => [map1_template, map2_template, ...]
}
}Example
input = %{
"user_profile" => %{
"#merge" => [
"{{$user}}",
%{"role" => "{{$role}}", "verified" => true}
]
}
}
context = %{
"$user" => %{"name" => "Alice", "email" => "alice@example.com"},
"$role" => "admin"
}
{:ok, result} = Mau.render_map(input, context)
# result: %{"user_profile" => %{
# "name" => "Alice",
# "email" => "alice@example.com",
# "role" => "admin",
# "verified" => true
# }}#if Directive
Conditionally render templates.
Syntax
# With else
%{
"key" => %{
"#if" => [condition, true_template, false_template]
}
}
# Without else
%{
"key" => %{
"#if" => [condition, true_template]
}
}Example
input = %{
"status" => %{
"#if" => [
"{{$user.premium}}",
%{"level" => "premium", "price" => "$9.99"},
%{"level" => "free", "price" => "free"}
]
}
}
context = %{
"$user" => %{"name" => "Alice", "premium" => true}
}
{:ok, result} = Mau.render_map(input, context)
# result: %{"status" => %{"level" => "premium", "price" => "$9.99"}}#pick Directive
Extract specific keys from a map.
Syntax
%{
"key" => %{
"#pick" => [map_template, keys]
}
}Example
input = %{
"public_profile" => %{
"#pick" => [
"{{$user}}",
["name", "email", "avatar"]
]
}
}
context = %{
"$user" => %{
"name" => "Alice",
"email" => "alice@example.com",
"avatar" => "alice.jpg",
"password_hash" => "secret" # Won't be included
}
}
{:ok, result} = Mau.render_map(input, context)
# result: %{"public_profile" => %{
# "name" => "Alice",
# "email" => "alice@example.com",
# "avatar" => "alice.jpg"
# }}#pipe Directive
Thread data through a series of transformations.
Syntax
%{
"key" => %{
"#pipe" => [initial_value, [directive1, directive2, ...]]
}
}How It Works
The piped value is automatically injected as the first argument to each directive. Access it via $self context variable.
Basic Example
input = %{
"result" => %{
"#pipe" => [
"{{$items}}",
[
%{"#filter" => "{{$loop.item.price > 100}}"},
%{"#map" => %{"name" => "{{$loop.item.name}}"}}
]
]
}
}
context = %{
"$items" => [
%{"name" => "Item A", "price" => 50},
%{"name" => "Item B", "price" => 150},
%{"name" => "Item C", "price" => 200}
]
}
{:ok, result} = Mau.render_map(input, context)
# result: %{"result" => [
# %{"name" => "Item B"},
# %{"name" => "Item C"}
# ]}Multi-Stage Pipeline
%{
"#pipe" => [
"{{$data}}",
[
%{"#filter" => "{{$loop.item.active}}"},
%{"#map" => %{"username" => "{{$loop.item.name}}"}},
%{"#filter" => "{{$loop.item.username != 'admin'}}"}
]
]
}Advanced Patterns
Combining Directives
Use multiple directives in nested structures:
input = %{
"report" => %{
"#merge" => [
%{"title" => "User Report"},
%{
"users" => %{
"#map" => [
"{{$users}}",
%{
"name" => "{{$loop.item.name}}",
"profile" => %{
"#if" => [
"{{$loop.item.premium}}",
%{"tier" => "premium"},
%{"tier" => "free"}
]
}
}
]
}
}
]
}
}Data Transformation Pipeline
Create data flows similar to Elixir pipes:
input = %{
"processed" => %{
"#pipe" => [
"{{$raw_data}}",
[
%{"#filter" => "{{not nil}}"}, # Remove nils
%{"#map" => %{ # Transform
"value" => "{{$loop.item | upper_case}}"
}},
%{"#filter" => "{{$loop.item.value}}"} # Filter empty
]
]
}
}Context Variables
Special variables available in templates:
{{$users}} # From context
{{$loop.item}} # Current item in map/filter
{{$loop.index}} # Current index
{{$self}} # Piped value (in pipe directive)Regular template expressions also work:
"count" => "{{items | length}}"
"active" => "{{user.verified and user.active}}"Complete Example: API Response Transformation
Transform an API response into a structured format:
input = %{
"data" => %{
"#merge" => [
%{
"timestamp" => "{{now}}",
"source" => "api"
},
%{
"users" => %{
"#pipe" => [
"{{$response.users}}",
[
%{"#filter" => "{{$loop.item.active}}"},
%{"#map" => %{
"id" => "{{$loop.item.id}}",
"name" => "{{$loop.item.full_name}}",
"contact" => %{
"#merge" => [
%{"email" => "{{$loop.item.email}}"},
%{"phone" => "{{$loop.item.phone}}"}
]
}
}}
]
]
}
}
]
}
}
context = %{
"now" => "2024-01-15T10:30:00Z",
"$response" => %{
"users" => [
%{
"id" => 1,
"full_name" => "Alice Johnson",
"email" => "alice@example.com",
"phone" => "555-0001",
"active" => true
},
%{
"id" => 2,
"full_name" => "Bob Smith",
"email" => "bob@example.com",
"phone" => "555-0002",
"active" => false
}
]
}
}
{:ok, result} = Mau.render_map(input, context)Result:
%{
"data" => %{
"timestamp" => "2024-01-15T10:30:00Z",
"source" => "api",
"users" => [
%{
"id" => 1,
"name" => "Alice Johnson",
"contact" => %{
"email" => "alice@example.com",
"phone" => "555-0001"
}
}
]
}
}Comparing render vs render_map
| Feature | render | render_map |
|---|---|---|
| Output Type | String | Map/Structure |
| Use Case | Text/HTML output | Data transformation |
| Syntax | {{ }} and {% %} | Directive maps |
| Iteration | {% for %} | #map directive |
| Filtering | | filter | #filter directive |
| Conditionals | {% if %} | #if directive |
Best Practices
- Validate Input Data
Always check if required data exists before transforming.
- Use Meaningful Directive Keys
%{
"active_users" => %{ # Clear intent
"#filter" => [...]
}
}- Break Down Complex Transformations
Use intermediate steps for clarity:
%{
"filtered" => %{
"#filter" => [...]
},
"transformed" => %{
"#map" => ["{{filtered}}", ...]
}
}- Comment Complex Structures
# Step 1: Filter active users
# Step 2: Map to names only
# Step 3: Sort results
%{"#pipe" => [...]}Troubleshooting
Directive Not Applied
Problem: Directive key not recognized
Solution: Verify syntax and ensure arguments are lists
# Wrong
%{"#map" => "{{items}}"}
# Right
%{"#map" => ["{{items}}", item_template]}Empty Results
Problem: Directive returns empty
Solution: Check collection is rendering correctly
# Debug: render collection first
{:ok, items} = Mau.render("{{$items}}", context)
IO.inspect(items)Type Errors
Problem: Wrong data type to directive
Solution: Ensure correct types:
#mapand#filterneed lists#mergeneeds maps#pickneeds map and key list
See Also
- Template Language Reference - Template expressions
- Variables Guide - Variable access
- Map Directives Reference - Complete directive reference
- Data Transformation Examples - Real-world map rendering examples