Docs / Mapper Ordering

Sort lines and mapped records with orderBy

Put purchase order lines, consolidated SKU totals, or delivery allocations into the order your output needs. Sort by a projected key while keeping the complete item available for the next mapping step.

Choose a key and direction

toJson(orderBy(value in [10,2,1] => value))
// [1,2,10]

toJson(orderBy(code in ["A","C","B"] => code,"desc"))
// ["C","B","A"]

The expression after => computes the sorting key. The result is a new list containing the original items. Ascending order is the default; the optional second argument accepts "asc" or "desc".

Equal keys keep their original order in both directions. Numeric 1 and 1.0, for example, tie. Duplicates remain in the result.

Sort PO1 lines, then read their fields

A purchase order may list lines in a different order from their line numbers. PO101 is text, so convert it explicitly when the partner uses numeric line numbers:

lines=orderBy(line in ST->PO1 => toNumber(line(01)))
toJson(forEach(line in lines => {
    "line":line(01),
    "quantity":line(02),
    "description":line->PID(05)
}))

Line numbers 20, 2, and 10 become 2, 10, and 20. Each item remains a segment: line(02) still reads its quantity and line->PID(05) still reads its associated description. Sorting by quantity instead can use the numeric X12 element directly: orderBy(line in ST->PO1 => line(02),"desc").

Order consolidated SKU totals

Combine repeated SKUs with groupBy and sumOf, then put the resulting records in SKU order:

lines=[{"sku":"B200","quantity":2},{"sku":"A100","quantity":1},{"sku":"B200","quantity":3}]
groups=groupBy(line in lines => line["sku"])
totals=forEach(group in groups => {
    "sku":key(group),
    "quantity":sumOf(line in val(group) => line["quantity"])
})
toJson(orderBy(total in totals => total["sku"]))
// [{"sku":"A100","quantity":1},{"sku":"B200","quantity":5}]

The same pattern works for other mapped records. Consistently formatted YYYY-MM-DD text dates sort chronologically:

deliveries=[{"date":"2026-10-06","quantity":50},{"date":"2026-10-02","quantity":20}]
toJson(orderBy(delivery in deliveries => delivery["date"]))
// [{"date":"2026-10-02","quantity":20},{"date":"2026-10-06","quantity":50}]

Use consistently numeric or text keys

Every key in one call must be a number, or every key must be text. Numbers sort numerically. Text sorts case-sensitively by UTF-16 code units, without locale collation: "10" comes before "2". Use toNumber(...) for numeric text or toLower(...) for case-insensitive text keys.

A selection containing exactly one X12 element is accepted as its numeric value or text. Lists, dictionaries, booleans, segments, and selections containing zero or multiple elements cannot serve as keys. Empty text is a valid key and comes first in ascending order.

Use the optional predicate after : to omit records before computing their keys. For example, skip empty text before numeric conversion:

toJson(orderBy(value in ["10","","2"] : !isEmpty(value) => toNumber(value)))
// ["2","10"]

The values remain strings because only the key is converted. For missing optional elements, use a filter or an explicit fallback such as firstNonEmpty(...). The source is evaluated once, then each predicate and matching item's key is evaluated in source order. The direction is evaluated once afterward. The iterator is local to the predicate and key; the source and direction use the outer scope.

Compose with other collections

Sources may be segment paths, element selections, lists, dictionaries, or expressions that return those collections. Empty sources and filters with no matches produce []. Required path assertions still apply: ST->N1->N3! and ST->N1->N3+ report E2604 when no N1 exists, since the required descendant has no matches. Use ? or an unsuffixed path when an empty result is acceptable. Feed the ordered list to forEach, find, sumOf, groupBy, or flatMap.

A dictionary source contributes one-entry dictionaries. Use key(entry) and val(entry) in the sorting expression:

toJson(orderBy(entry in {"b":2,"a":1} => key(entry)))
// [{"a":1},{"b":2}]

When iterating the resulting list, key(item) is the new list index. Assign entry=item inside that loop to read the stored dictionary key with key(entry).

Sorting creates a new outer list, so changing its order or replacing an item does not modify the source list. Nested lists and dictionaries remain shared: editing a nested record changes that same record in both places. Element-source items become scalar values that keep their numeric type and original text formatting; segment-source items retain their segment navigation.

Resolve sorting errors

  • E4701: The key is not a number, text, or single X12 element. For orderBy(n in [1] => [n]), change the key to => n.
  • E4702: Numeric and text keys are mixed. For orderBy(n in [1,"2"] => n), use => toNumber(n) if numeric order is intended.
  • E4703: The direction is not "asc" or "desc". For orderBy(n in [1] => n,"down"), replace "down" with "desc".

Run these patterns on a sample in the Mapper workspace, then save the expected ordering as a mapping test case. The Mapper Built-ins reference includes the complete signature, examples, and error guidance.