# Operators URL: /docs/theming/templating/kida/syntax/operators/ Section: syntax Tags: reference, kida, syntax -------------------------------------------------------------------------------- Operators Kida adds modern operators for data transformation and null-safe access. Pipeline Operator (|>) The pipeline operator chains filters left-to-right: {% let recent_posts = site.pages |> where('type', 'blog') |> where('draft', false) |> sort_by('date', reverse=true) |> take(10) %} Pipeline vs Pipe Both | and |> compile to identical code: {# These are functionally identical #} {{ items |> where('published', true) |> sort_by('date') |> take(5) }} {{ items | where('published', true) | sort_by('date') | take(5) }} Syntax Convention | Inline expressions, Jinja2 familiarity |> Multiline pipelines, functional style Note Note You cannot mix | and |> in the same expression. Pick one per chain. Multiline Pipelines Add inline comments to explain each step: {% let recent_posts = site.pages |> where('type', 'blog') {# Only blog posts #} |> where('draft', false) {# Exclude drafts #} |> sort_by('date', reverse=true) {# Newest first #} |> take(10) %} {# Limit to 10 #} Filter Name Mapping Common Jinja2 patterns and their Kida equivalents: Jinja2 Filter Kida Filter Description selectattr('key') where('key', true) Boolean filter selectattr('key', 'eq', val) where('key', val) Equality filter rejectattr('key') where_not('key', true) Inverse boolean sort(attribute='key') sort_by('key') Sort by attribute batch(n) | first take(n) Get first n items (both return a list of n items; take() avoids grouping overhead) groupby('key') group_by('key') Group by attribute Note Note About batch(n) | first vs take(n): While both return the first n items, batch(n) groups all items into batches first, then first extracts the first batch. The take(n) filter directly takes the first n items without grouping, making it more efficient for this use case. Optional Chaining (?.) Safe navigation through potentially null values: {{ user?.profile?.name ?? 'Anonymous' }} {{ page?.metadata?.author?.avatar }} {{ config?.social?.twitter?.handle }} Compare to Jinja2's defensive coding: {{ user.profile.name if user and user.profile and user.profile.name else 'Anonymous' }} Null Coalescing (??) Concise fallback for null/undefined values: {{ page.subtitle ?? page.title }} {{ user.nickname ?? user.name ?? 'Guest' }} {{ config.theme ?? 'default' }} Precedence Gotcha The ?? operator has lower precedence than |, so filters bind to the fallback: {# ❌ Parses as: items ?? ([] | length) — returns list, not length! #} {{ items ?? [] | length }} {# ✅ Correct: use | default() for filter chains #} {{ items | default([]) | length }} Rule: Use ?? for simple direct output. Use | default() when applying filters after the fallback. Combined Usage {{ page?.metadata?.image ?? site?.config?.default_image ?? '/images/placeholder.png' }} Best Practices Filter Early, Limit Early {# ✅ Good: Filter before sorting #} {% let posts = site.pages |> where('type', 'blog') |> where('draft', false) |> sort_by('date') |> take(10) %} {# ❌ Less efficient: Sort everything first #} {% let posts = site.pages |> sort_by('date') |> where('type', 'blog') |> take(10) %} Use Multiline for Complex Chains {# 3+ filters: use multiline #} {% let posts = site.pages |> where('type', 'blog') |> sort_by('date', reverse=true) |> take(5) %} {# Simple chains: inline is fine #} {{ items |> take(3) }} Complete Example {% extends "baseof.html" %} {% let post = page %} {% let author = post?.metadata?.author ?? site?.config?.default_author ?? 'Anonymous' %} {% let post_image = post?.metadata?.image ?? post?.metadata?.cover %} {% let related_posts = site.pages |> where('type', 'blog') |> where('tags', post.tags | first) |> where_not('_path', post._path) |> sort_by('date', reverse=true) |> take(3) %} {% block content %} <article class="blog-post"> {% if post_image %} <img src="{{ post_image }}" alt="{{ post.title }}"> {% end %} <header> <h1>{{ post.title }}</h1> <span>By {{ author }}</span> </header> {{ post.content | safe }} {% if related_posts | length > 0 %} <aside class="related"> <h2>Related Posts</h2> {% for item in related_posts %} <a href="{{ item.url }}">{{ item.title }}</a> {% end %} </aside> {% end %} </article> {% endblock %} -------------------------------------------------------------------------------- Metadata: - Author: lbliii - Word Count: 603 - Reading Time: 3 minutes