indexing and lookup
This structure is good for filtering, but bad for lookup.
The problem we are solving
Each FamilyInstance references its type like this:
"parent": {
"id": 617464,
"type": "FamilySymbol"
}
For every instance, we need to answer:
“Give me the FamilySymbol with ID
617464.”
❌ Without an index (slow and noisy)
You would have to search the array every time:
$symbols[id = parent.id]
Problems with this approach:
- Re-scans the entire symbol list for every instance
- Becomes slow on large models
- Makes the rule harder to read
- Hides intent (“this is a lookup, not a filter”)
What indexing does instead
This expression:
$symbols.{$string(id): $}
Transforms the array into key–value pairs:
{
"617464": { "id": 617464, "values": { ... } },
"617670": { "id": 617670, "values": { ... } },
"617812": { "id": 617812, "values": { ... } }
}
Then:
$merge(...)
Combines those pairs into one object.
The result: $symIndex
{
"617464": { ...FamilySymbol object... },
"617670": { ...FamilySymbol object... }
}
Now you can do direct lookup:
$lookup($symIndex, $string(parent.id))
This is:
- Constant-time lookup
- Explicit intent
- Easy to debug
- Scales well to large models
Why $string(id) is required
JSON object keys must be strings.
FamilySymbol IDs are numbers.
So we convert:
$string(id)
This ensures:
- Valid object keys
- Correct matching with
parent.id
Rule of thumb for users
Lists are for selection. Maps are for lookup.
If you find yourself repeatedly searching a list by ID, you should probably build an index first.
Why this pattern is preferred in DAQS rules
Indexing:
- Improves performance
- Makes rules easier to read
- Prevents accidental multiple matches
- Clarifies intent for reviewers
This is not an optimisation trick — it is the correct data structure for the problem.