Skip to content

Preserve key order when expanding included mappings - #37

Merged
tuckermcclure merged 3 commits into
mainfrom
tucker/fix-key-order-in-includes
Aug 24, 2026
Merged

tuckermcclure merged 3 commits into
mainfrom
tucker/fix-key-order-in-includes

Conversation

@tuckermcclure

Copy link
Copy Markdown
Member

Summary

This PR preserves mapping key order when expanding YAML and JSON includes.

Previously, include merging used:

union(keys(payload), keys(d))

Although both inputs are OrderedDicts, union returns a Set. Iterating that set introduces hash ordering, and the resulting OrderedDict preserves the incorrect order.

This is particularly visible when the expanded mapping is converted to a NamedTuple, because field order contributes to the resulting Julia type.

Changes

The include merge now:

  1. Copies the included payload in its existing order.
  2. Applies values from the including mapping in their existing order.
  3. Overwrites existing keys without moving them.
  4. Appends newly introduced keys at the end.
  5. Omits the include directive itself from the result.

For example:

# base.yaml
first: 1
second: 2
third: 3
# override.yaml
include: base.yaml
second: 20
fourth: 4

The expanded result retains this order:

["first", "second", "third", "fourth"]

The value of second is updated without moving the key, and fourth is appended.

NamedTuple behavior

The regression coverage compares equivalent inline and included YAML using an explicitly nested NamedTuple type:

T = NamedTuple{(:values,), Tuple{NamedTuple}}

inline = load_from_yaml("inline.yaml", T)
included = load_from_yaml("included.yaml", T)

keys(included.values) == (:first, :second, :third)
typeof(included) === typeof(inline)
included == inline

The nested annotation is explicit because dictionaries decoded through an untyped Any field remain OrderedDicts.

Testing

Added regression coverage for:

  • Included YAML mappings.
  • Included JSON mappings.
  • Nested includes.
  • Overridden keys retaining their positions.
  • New keys being appended in local-file order.
  • Equivalent inline and included mappings producing identical NamedTuple field order, type, and value.

The complete PortableStructs test suite passes.

Closes #31

@tuckermcclure
tuckermcclure merged commit 29e7072 into main Aug 24, 2026
6 checks passed
@tuckermcclure
tuckermcclure deleted the tucker/fix-key-order-in-includes branch August 24, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preserve mapping order when expanding includes

2 participants