Comprehensions Guide¶
List and dictionary comprehensions provide concise syntax for creating collections. Miya Engine supports basic comprehensions with some limitations compared to Jinja2.
Working Example: See
examples/features/comprehensions/for complete examples.
Table of Contents¶
List Comprehensions¶
Basic Syntax¶
Transform collections in a single line:
Simple Transformations¶
{# Double each number #}
{{ [x * 2 for x in numbers] }}
→ [2, 4, 6, 8, 10]
{# Convert to uppercase #}
{{ [name|upper for name in names] }}
→ ["ALICE", "BOB", "CHARLIE"]
{# Extract property #}
{{ [user.name for user in users] }}
→ ["Alice", "Bob", "Charlie"]
With Filters¶
Apply template filters in comprehensions:
{# Title case all names #}
{{ [name|title for name in names] }}
→ ["Alice", "Bob", "Charlie"]
{# Format prices #}
{{ ["$" ~ (price|round(2)) for price in prices] }}
→ ["$9.99", "$19.99", "$29.99"]
{# Slugify titles #}
{{ [title|slugify for title in titles] }}
→ ["hello-world", "product-name", "user-guide"]
{# Chain multiple filters #}
{{ [name|trim|upper for name in names] }}
→ ["ALICE", "BOB", "CHARLIE"]
Arithmetic Operations¶
{# Apply discount #}
{{ [price * 0.9 for price in prices] }}
→ [8.99, 17.99, 26.99]
{# Calculate with expressions #}
{{ [item.price * item.qty for item in cart] }}
String Operations¶
{# Format user display #}
{{ [user.name ~ " (" ~ user.role ~ ")" for user in users] }}
→ ["Alice (admin)", "Bob (user)", "Charlie (moderator)"]
{# Concatenate with filter #}
{{ [name|upper for name in names] }}
→ ["ALICE", "BOB", "CHARLIE"]
Dictionary Comprehensions¶
Basic Syntax¶
Create dictionaries from iterables:
Simple Mappings¶
{# Create ID to name mapping #}
{{ {user.id: user.name for user in users} }}
→ {1: "Alice", 2: "Bob", 3: "Charlie"}
{# Email to role mapping #}
{{ {user.email: user.role for user in users} }}
→ {"alice@ex.com": "admin", "bob@ex.com": "user"}
{# Product SKU to name #}
{{ {product.sku: product.name for product in products} }}
→ {"LAP001": "Laptop", "MOU002": "Mouse"}
With Transformations¶
{# Uppercase keys #}
{{ {key|upper: value for key, value in data} }}
{# Format values #}
{{ {product.id: "$" ~ product.price for product in products} }}
→ {1: "$999", 2: "$29", 3: "$79"}
Limitations¶
Inline If Clauses Not Supported¶
Jinja2 syntax (NOT in Miya):
{# Does NOT work in Miya #}
{{ [x for x in numbers if x > 5] }}
{{ [user.name for user in users if user.active] }}
{{ {k: v for k, v in dict.items() if v > 0} }}
Error: Comprehensions with inline if clauses are not supported in Miya Engine.
Dict Unpacking with .items() Not Supported¶
Jinja2 syntax (NOT in Miya):
{# Does NOT work in Miya #}
{{ {k: v for k, v in data.items()} }}
{{ {k.upper(): v * 2 for k, v in config.items()} }}
Error: Tuple unpacking in dict comprehensions is not supported.
Nested Comprehensions Not Supported¶
Jinja2 syntax (NOT in Miya):
{# Does NOT work in Miya #}
{{ [item for sublist in lists for item in sublist] }}
{{ [x * y for x in range(3) for y in range(3)] }}
Error: Multiple for clauses are not supported.
Workarounds¶
Alternative 1: Use Template Filters¶
Instead of inline if, use filters to pre-filter data:
{# Not supported #}
{{ [x for x in numbers if x > 5] }}
{# Use select filter #}
{{ numbers|select("greaterthan", 5)|list }}
{# Not supported #}
{{ [user.name for user in users if user.active] }}
{# Use selectattr filter #}
{{ users|selectattr("active")|map(attribute="name")|list }}
{# Not supported #}
{{ [user for user in users if user.age >= 18] }}
{# Use selectattr + list #}
{{ users|selectattr("age", ">=", 18)|list }}
Alternative 2: Use For Loops¶
For complex filtering, use traditional loops:
{# Not supported #}
{{ [x * 2 for x in numbers if x is even] }}
{# Use for loop with condition #}
{% set result = [] %}
{% for x in numbers %}
{% if x is even %}
{% set _ = result.append(x * 2) %}
{% endif %}
{% endfor %}
{{ result }}
{# Or use loop filtering #}
{% for x in numbers if x is even %}
{{ x * 2 }}{{ ", " if not loop.last else "" }}
{% endfor %}
Alternative 3: Prepare Data in Application¶
The best approach for complex filtering:
In Go code:
// Filter data before passing to template
activeUsers := []User{}
for _, user := range users {
if user.Active && user.Age >= 18 {
activeUsers = append(activeUsers, user)
}
}
ctx.Set("active_users", activeUsers)
In template:
Alternative 4: Use Set with Block Assignment¶
{# Collect filtered results #}
{% set filtered_names %}
{% for user in users if user.active %}
{{ user.name }}{{ "," if not loop.last else "" }}
{% endfor %}
{% endset %}
{# Split back to list if needed #}
{{ filtered_names|trim|split(",") }}
Practical Examples¶
Real-World: Shopping Cart Summary¶
{# Calculate line totals using list comprehension #}
{% set cart_items = [{"name": item.product.name, "price": item.price, "qty": item.quantity, "total": item.price * item.quantity} for item in cart.items] %}
{% set subtotal = [item.total for item in cart_items]|sum %}
{% set tax = subtotal * 0.08 %}
<div class="cart-summary">
<h3>Order Summary</h3>
{% for item in cart_items %}
<div class="cart-item">
<span>{{ item.name }} ({{ item.qty }}x)</span>
<span>${{ item.total }}</span>
</div>
{% endfor %}
<div class="totals">
<div>Subtotal: ${{ subtotal }}</div>
<div>Tax: ${{ tax|round(2) }}</div>
<div class="total">Total: ${{ (subtotal + tax)|round(2) }}</div>
</div>
</div>
Real-World: Dynamic Form Fields¶
{# Extract required fields and field types using comprehensions #}
{% set required_fields = [field.name for field in form_schema.fields] %}
{% set field_types = {field.name: field.type for field in form_schema.fields} %}
<form id="dynamic-form" method="post">
{% for field in form_schema.fields %}
<div class="form-group">
<label for="{{ field.name }}">{{ field.label }}</label>
<input type="{{ field_types[field.name] }}"
name="{{ field.name }}"
id="{{ field.name }}">
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
Example 1: Extract Names¶
{# Simple property extraction #}
{% set user_names = [user.name for user in users] %}
<p>Users: {{ user_names|join(", ") }}</p>
{# With filter #}
{% set user_names = [user.name|title for user in users] %}
<p>Users: {{ user_names|join(", ") }}</p>
Example 2: Calculate Totals¶
{# Calculate line totals #}
{% set line_totals = [item.price * item.qty for item in cart] %}
<p>Line Totals: {{ line_totals }}</p>
<p>Cart Total: ${{ line_totals|sum|round(2) }}</p>
{# With discount #}
{% set discounted = [price * 0.9 for price in prices] %}
<p>Discounted Prices: {{ discounted }}</p>
Example 3: Create Lookups¶
{# Create ID lookup dictionary #}
{% set user_lookup = {user.id: user.name for user in users} %}
{# Use the lookup #}
<p>User 123: {{ user_lookup[123] }}</p>
{# Email to role mapping #}
{% set role_map = {user.email: user.role for user in users} %}
<p>alice@example.com is: {{ role_map["alice@example.com"] }}</p>
Example 4: Format Display Names¶
{# Create formatted names #}
{% set display_names = [user.name ~ " <" ~ user.email ~ ">" for user in users] %}
<select name="recipient">
{% for name in display_names %}
<option>{{ name }}</option>
{% endfor %}
</select>
Example 5: Nested Data (Use Loops)¶
{# Nested comprehension not supported #}
{# {{ [item.name for category in categories for item in category.items] }} #}
{# Use nested loops instead #}
{% for category in categories %}
<h3>{{ category.name }}</h3>
<ul>
{{ [item.name for item in category.items]|join(", ") }}
</ul>
{% endfor %}
Example 6: Filter Then Comprehend¶
{# First filter with selectattr, then comprehend #}
{% set active_users = users|selectattr("active")|list %}
{% set active_emails = [user.email for user in active_users] %}
<p>Active user emails: {{ active_emails|join(", ") }}</p>
{# Or chain it all #}
{{ users|selectattr("active")|map(attribute="email")|join(", ") }}
Example 7: Conditional Value Transformation¶
{# Using ternary in comprehension #}
{% set statuses = [
"Active" if user.active else "Inactive"
for user in users
] %}
<ul>
{% for status in statuses %}
<li>{{ status }}</li>
{% endfor %}
</ul>
Example 8: Working with Zip (Use For Loop)¶
Tuple unpacking (for a, b in zip(...)) is not supported in comprehensions. Use a {% for %} loop instead:
{# Combine multiple lists — use for loop (supports unpacking) #}
{% set names = ["Alice", "Bob", "Charlie"] %}
{% set scores = [95, 87, 92] %}
<ul>
{% for name, score in zip(names, scores) %}
<li>{{ name }}: {{ score }}</li>
{% endfor %}
</ul>
Feature Comparison¶
| Feature | Syntax | Status | Alternative |
|---|---|---|---|
| Basic List | [expr for x in list] |
Supported | - |
| List with Filter | [expr for x in list if cond] |
Not supported | Use selectattr/select filters |
| Basic Dict | {expr: expr for x in list} |
Supported | - |
| Dict with .items() | {k: v for k, v in dict.items()} |
Not supported | Loop over list instead |
| Dict with Filter | {k: v for x in list if cond} |
Not supported | Pre-filter with selectattr |
| Nested | [x for list in lists for x in list] |
Not supported | Use nested loops |
| With Filters | [x|filter for x in list] |
Supported | - |
| With Expressions | [x * 2 for x in list] |
Supported | - |
| With Zip (unpacking) | [a + b for a, b in zip(l1, l2)] |
Not supported | Use {% for %} loop with unpacking |
| With Ternary | ["yes" if x else "no" for x in list] |
Supported | - |
Best Practices¶
1. Use Filters for Filtering¶
{# Good - use filters #}
{{ users|selectattr("active")|map(attribute="name")|list }}
{# Avoid - inline if not supported #}
{{ [user.name for user in users if user.active] }}
2. Keep Comprehensions Simple¶
{# Good - simple transformation #}
{{ [x * 2 for x in numbers] }}
{# Avoid - too complex (tuple unpacking and inline if not supported) #}
{# {{ [complex_function(x, y, z) for x, y, z in zip(a, b, c) if condition] }} #}
3. Pre-filter in Application Code¶
{# Good - filter in Go, comprehend in template #}
# Go: ctx.Set("active_users", filterActiveUsers(users))
{{ [user.name for user in active_users] }}
{# Avoid - trying to filter in comprehension #}
{{ [user.name for user in users if user.active] }}
4. Use Descriptive Variable Names¶
{# Good - clear purpose #}
{% set user_emails = [user.email for user in users] %}
{# Avoid - unclear #}
{% set x = [u.e for u in users] %}
Summary¶
Comprehensions in Miya Engine: ~70% Jinja2 Compatible
Fully Supported:
- Basic list comprehensions [expr for x in list]
- Basic dict comprehensions {k_expr: v_expr for x in list}
- Comprehensions with filters [x|filter for x in list]
- Comprehensions with expressions [x * 2 for x in list]
- Comprehensions with zip/enumerate
- Nested property access [user.name for user in users]
- String concatenation in comprehensions
- Ternary operators in comprehensions
Not Supported:
- Inline if clauses [x for x in list if condition]
- Dict comprehensions with .items() unpacking {k: v for k, v in dict.items()}
- Nested comprehensions [x for list in lists for x in list]
- Complex conditional filtering in comprehensions
Workarounds Available:
- Use selectattr/select/reject filters for filtering
- Use traditional for loops for complex logic
- Pre-filter data in application code
- Use filter chains: items|selectattr("active")|map(attribute="name")|list
Miya's comprehension support covers the most common use cases. For advanced filtering, use the powerful filter system or prepare data in your application.
See Also¶
- Working Example - Complete comprehensions demo
- Filters Guide - Using selectattr/select for filtering
- Control Structures - Alternative loop syntax
- Miya Limitations - All known limitations