YAML 04 📄 Mapping Sequences and Dictionaries
YAML represents structured data through three primitive structures: scalars, sequences, and mappings . A mapping is a set of key-value pairs—what other languages call a dictionary, hash, or object. A sequence is an ordered list of values—an array or list. The two compose freely: a mapping value can be a sequence, a sequence item can be a mapping, and either can nest inside the other to arbitrary depth.
This chapter covers the syntax for both structures in block style, the nesting patterns that combine them, and the shortcuts that reduce verbosity without changing meaning. It explains why indentation is the only structural signal, how the dash and colon separators work, and the merge key that lets one mapping inherit another’s entries. The sections that follow walk through sequences, mappings, their combination, and the reuse mechanisms.
Key point: Mappings use key: value pairs and sequences use - item entries; nesting is expressed by indentation alone, and every child must be indented more than its parent while siblings share identical indentation.
Why mappings and sequences exist
The structure problem. A configuration file is rarely a flat list of values. A Kubernetes manifest has a metadata mapping, a spec mapping, and inside spec a containers sequence of mappings. Without mappings and sequences that nest, every configuration would flatten into dotted keys like spec.containers.0.image, which is unreadable and error-prone. YAML’s block syntax mirrors the data’s own hierarchy: the indentation you see is the nesting the parser reads .
The ordering distinction. Mappings are unordered; the order of keys does not affect meaning, though most parsers preserve insertion order for readability . Sequences are ordered; the position of each item is part of the data. This distinction matters when converting between YAML and JSON or when writing code that iterates a mapping versus an array.
The merge reuse problem. Configuration files repeat the same defaults across multiple services. Without a reuse mechanism, every service block re-declares the same timeout and retry settings, and a change to the default requires editing every block. YAML’s merge key << inserts the entries of one mapping into another, so shared defaults live in one anchor and each service overrides only what differs .
a. Sequences: block and flow syntax
A block sequence places each item on its own line, prefixed with a dash and a space. The dash counts as indentation, which is why a sequence nested under a key can sit at the same column as the key’s value without extra spaces .
# Block sequence: one dash per item
environments:
- development
- staging
- production
# Sequence of sequences: empty dash, then indented items
matrix:
- - 1
- 2
- - 3
- 4
# Flow sequence: inline brackets
roles: [admin, editor, viewer]
The space after the dash is mandatory. -item without a space is not a sequence entry; it is a plain scalar beginning with a hyphen. The same rule applies to the colon in mappings: key:value without a space after the colon is a single string, not a key-value pair .
b. Mappings: key-value pairs and nesting
A block mapping writes each key on its own line, followed by a colon and a space. The value can be a scalar, another mapping, or a sequence. Nested mappings are indented under their parent key.
# Flat mapping
database:
host: db.example.com
port: 5432
# Nested mapping: credentials is a child of database
database:
host: db.example.com
credentials:
username: app_user
password: secret
# Flow mapping: inline braces
point: { x: 1, y: 2 }
A mapping value can be a sequence. The sequence can start on the same line as the key or on the next line, indented. Both forms parse identically.
# Sequence on the next line
allowed_hosts:
- localhost
- example.com
# Flow sequence inline
allowed_hosts: [localhost, example.com]
c. Combining mappings and sequences
The most common real-world structure is a sequence of mappings: a list where each item is a dictionary. This is the shape of a Kubernetes containers list, a Docker Compose services block, and an Ansible task list.
# Sequence of mappings: each dash starts a new mapping
services:
- name: web
image: nginx:latest
ports:
- "80:80"
- name: db
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
The indentation rule is precise: the keys within one mapping must align with each other, and the dash of the next item must align with the dash of the first . A key indented under another key becomes its child; a key at the same indentation is its sibling.
The merge key << reuses a mapping’s entries. An anchor marks the source mapping; an alias references it; the merge key inserts its pairs into the target .
# Define defaults once with an anchor
defaults: &defaults
timeout: 30
retries: 3
# Merge defaults into each service; endpoint overrides nothing
service_a:
<<: *defaults
endpoint: /a
service_b:
<<: *defaults
endpoint: /b
# service_a and service_b both have timeout: 30 and retries: 3
Complete Example Session
# ============================================
# PART 1: SEQUENCE OF SCALARS
# ============================================
regions:
- us-east-1
- eu-west-1
- ap-southeast-1
# ============================================
# PART 2: MAPPING OF SCALARS
# ============================================
server:
host: 0.0.0.0
port: 8080
debug: false
# ============================================
# PART 3: MAPPING CONTAINING A SEQUENCE
# ============================================
firewall:
allowed_ports:
- 80
- 443
- 22
# ============================================
# PART 4: SEQUENCE OF MAPPINGS (THE COMMON SHAPE)
# ============================================
containers:
- name: app
image: myapp:1.0
env:
- name: NODE_ENV
value: production
- name: sidecar
image: proxy:2.1
# ============================================
# PART 5: MERGE KEY FOR SHARED DEFAULTS
# ============================================
defaults: &defaults
timeout: 30
retries: 3
api:
<<: *defaults
endpoint: /api
worker:
<<: *defaults
endpoint: /worker
timeout: 60 # override just this one
# ============================================
# PART 6: THE PARSED MENTAL MODEL
# ============================================
# containers: [
# { name: "app", image: "myapp:1.0",
# env: [ { name: "NODE_ENV", value: "production" } ] },
# { name: "sidecar", image: "proxy:2.1" }
# ]
# api: { timeout: 30, retries: 3, endpoint: "/api" }
# worker: { timeout: 60, retries: 3, endpoint: "/worker" }
The parts covered scalar sequences, scalar mappings, a mapping containing a sequence, the sequence-of-mappings shape, merge key reuse with an override, and the parsed data model.
Quick Reference
Structural Syntax
| Structure | Block Syntax | Flow Syntax |
|---|---|---|
| Sequence | - item per line | [item1, item2] |
| Mapping | key: value per line | {key1: v1, key2: v2} |
| Nested sequence | Dash at same column | [[1, 2], [3, 4]] |
| Sequence of mappings | Dash, then indented keys | [{k: v}, {k: v}] |
Nesting Rules
| Rule | Detail |
|---|---|
| Child indentation | Greater than parent |
| Sibling alignment | Identical column |
| Dash as indent | Counts as structural indentation |
| Space after dash | Required (- item, not -item) |
| Space after colon | Required (key: value, not key:value) |
Merge Key
| Element | Syntax | Purpose |
|---|---|---|
| Anchor | &name | Mark a mapping for reuse |
| Alias | *name | Reference the anchored mapping |
| Merge | <<: *name | Insert entries into current mapping |
| Override | key: value after << | Replace merged value |
Best Practices
✅ Do This:
# Align sibling keys exactly
server:
host: localhost
port: 8080 # ✅
# Indent sequence items under their key
ports:
- "80:80"
- "443:443" # ✅
# Use merge for shared defaults
defaults: &d
timeout: 30
api: { <<: *d, endpoint: /api } # ✅
# Quote port mappings (colon trap)
ports:
- "3000:3000" # ✅
❌ Don’t Do This:
# Misaligned sibling
server:
host: localhost
port: 8080 # ❌
# Missing space after dash
items:
-one # ❌ (scalar "-one")
# Missing space after colon
key:value # ❌ (single scalar)
# Unquoted colon in port mapping
ports:
- 3000:3000 # ❌ (colon trap)
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Sequence items parsed as scalars | No space after dash | - item, not -item |
| Mapping key parsed as string | No space after colon | key: value, not key:value |
| Key becomes child of another | Sibling indentation off | Align siblings at same column |
| Port mapping fails | Unquoted colon in value | Quote as "3000:3000" |
| Merge does not apply | Anchor and alias mismatch | Check &name and *name spelling |
| Sequence-of-mappings misaligned | Second dash not at first dash’s column | Align all dashes in the sequence |
Real-World Examples
1. Scalar sequence
tags: [yaml, config, data]
2. Scalar mapping
host: localhost
port: 8080
3. Mapping with sequence value
allowed_ips:
- 10.0.0.1
- 10.0.0.2
4. Sequence of mappings
users:
- name: Ana
role: admin
- name: Ben
role: viewer
5. Nested mapping
database:
credentials:
username: app
6. Merge key
base: &base
retries: 3
service: { <<: *base, url: /svc }
7. Flow sequence in mapping
roles: [admin, editor]
8. Sequence containing sequence
matrix:
- [1, 2]
- [3, 4]
9. Quoted port mapping
ports:
- "8080:80"
10. Kubernetes containers shape
containers:
- name: app
image: nginx
ports:
- containerPort: 80
Visual
Sequence vs Mapping
┌─────────────────────────────────────────────────────────┐
│ SEQUENCE (ordered list) │
│ │
│ - apple │
│ - banana │
│ - carrot │
│ │ │
│ ▼ │
│ ["apple", "banana", "carrot"] │
│ Position matters │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ MAPPING (key-value pairs) │
│ │
│ name: Billy │
│ age: 16 │
│ │ │
│ ▼ │
│ { name: "Billy", age: 16 } │
│ Order does not affect meaning │
└─────────────────────────────────────────────────────────┘
Sequence of Mappings
┌─────────────────────────────────────────────────────────┐
│ SEQUENCE OF MAPPINGS │
│ │
│ services: │
│ - name: web ◄── dash starts item 1 │
│ image: nginx keys align under dash │
│ - name: db ◄── dash aligns with first │
│ image: postgres keys align under dash │
│ │
│ Parsed: │
│ [ │
│ { name: "web", image: "nginx" }, │
│ { name: "db", image: "postgres" } │
│ ] │
└─────────────────────────────────────────────────────────┘
Merge Key Flow
┌─────────────────────────────────────────────────────────┐
│ MERGE KEY │
│ │
│ defaults: &defaults ──┐ │
│ timeout: 30 │ │
│ retries: 3 │ │
│ │ │
│ api: │ │
│ <<: *defaults ◄─────────┘ inserts both pairs │
│ endpoint: /api │
│ │
│ Result: │
│ api: { timeout: 30, retries: 3, endpoint: "/api" } │
│ │
│ Override after merge: │
│ worker: │
│ <<: *defaults │
│ timeout: 60 ◄── replaces merged timeout │
└─────────────────────────────────────────────────────────┘
Indentation as Structure
┌─────────────────────────────────────────────────────────┐
│ NESTING DEPTH │
│ │
│ root: ◄── column 0 │
│ child: ◄── column 2 (child of root) │
│ grandchild: ◄── column 4 (child of child) │
│ sibling: ◄── column 2 (sibling of child) │
│ │
│ Same column = sibling │
│ Deeper column = child │
│ Shallower = closes current scope │
└─────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Sequence syntax | - item per line, space required |
| Mapping syntax | key: value per line, space required |
| Flow sequence | [item1, item2] |
| Flow mapping | {key1: v1, key2: v2} |
| Nesting | Indentation only; children deeper than parent |
| Sequence of mappings | Dash, then indented keys; align dashes |
| Merge key | <<: *anchor inserts mapping entries |
| Merge override | Declare key: value after << |
| Dash as indent | Counts as structural indentation |
Key takeaways:
- Mappings and sequences are the two collection types. Mappings are key-value pairs (dictionaries); sequences are ordered lists. Every non-scalar YAML structure is one of these or a combination of them .
- The dash and the colon require trailing spaces.
-itemis a scalar beginning with a hyphen;key:valueis a single string. The space is the separator, not the punctuation alone . - Nesting is indentation, and alignment is meaning. A child is indented more than its parent; siblings share the same column. A one-space misalignment changes which node a key belongs to.
- Sequence of mappings is the workhorse shape. Kubernetes containers, Docker Compose services, and Ansible tasks all use the pattern where each dash starts a mapping and the keys align under that dash .
- The merge key reuses mappings without duplication. An anchor marks the source, an alias references it, and
<<inserts its pairs into the target. Overrides declared after the merge replace the inherited values .
Remember: YAML’s collection syntax is small: dash for sequence, colon for mapping, indentation for nesting. The failures come from the spaces—a missing space after a dash, a colon without a following space, a sibling one column out of alignment. When a file parses incorrectly, check the dash-space and colon-space first, then verify that siblings align and children are indented. The structure you see is the structure the parser reads; there is no other signal.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!