This example demonstrates using field ordinals (indices) for faster reading of large files with sequences.
Deserializes sequence metadata from an XML string and reads the same pet data as read-sequence example, but uses ordinals instead of field names for better performance.
An ordinal is the index of a field in the current record. For example, in a record with fields [Type, Name, RunningSpeed], the ordinals are:
When accessing fields by name (e.g., reader.getFieldByName("Type")), the library must:
When using ordinals (e.g., reader.fieldList.get(0)), you:
For large files with many records, this performance difference adds up significantly.
FtXmlMetaSerialization.deserialize()npx tsx examples/read-sequence-ordinal/index.ts
Reading pets with sequences using ordinals (faster):
1,1: 1,Misty,45
2,1: 1,Oscar,35
3,2: 2,Buddy,0.5,35,false
4,3: 2,Charlie,2,48,true,John,32
5,4: 2,Max,0.5,30,false
6,5: 3,Bubbles,Orange,Wen
7,5: 3,Flash,Yellow,Crucian
Note: Using ordinals is faster than field names for large files
because it avoids name lookup on every field access.
The first number is record count, second is table count.
Use ordinals when:
Use field names when:
let recOrdinals: number[] | null = null;
do {
while (reader.read()) {
if (recOrdinals === null) {
// Calculate ordinals once per table
recOrdinals = calculateRecordOrdinals(reader);
}
// Use ordinals for fast access
const value = reader.fieldList.get(recOrdinals[i]).value;
}
// Reset ordinals when table changes
recOrdinals = null;
} while (reader.nextResult());
// Read Sequence with Ordinals Example
// Demonstrates using field ordinals for faster reading of large files with sequences
// An ordinal is the index of a field in the current record
import { FtReader, FtXmlMetaSerialization } from "@pbkware/fielded-text-web";
// Sample CSV data with sequences
const csvData = `1,Misty,45
1,Oscar,35
2,Buddy,0.5,35,False
2,Charlie,2,48,True,John,32
2,Max,0.5,30,False
3,Bubbles,Orange,Wen
3,Flash,Yellow,Crucian`;
// Define Type values
const CatType = BigInt(1);
const DogType = BigInt(2);
const GoldFishType = BigInt(3);
// Define Field Names
const TypeFieldName = "Type";
const NameFieldName = "Name";
const RunningSpeedFieldName = "RunningSpeed";
const WalkDistanceFieldName = "WalkDistance";
const TrainingFieldName = "Training";
const TrainerFieldName = "Trainer";
const SessionCostFieldName = "SessionCost";
const ColorFieldName = "Color";
const ChineseClassificationFieldName = "ChineseClassification";
// Load metadata from XML string
const metaXml = `<?xml version="1.0" encoding="utf-8"?>
<FieldedText>
<Field DataType="Integer" Name="Type" />
<Field Id="1" Name="Name" />
<Field DataType="Float" Id="2" Name="RunningSpeed" />
<Field DataType="Float" Id="3" Name="WalkDistance" />
<Field DataType="Boolean" Id="4" Name="Training" />
<Field Id="5" Name="Trainer" />
<Field DataType="Decimal" Id="6" Name="SessionCost" />
<Field Id="7" Name="Color" />
<Field Id="8" Name="ChineseClassification" />
<Sequence Name="Root" Root="True">
<Item FieldIndex="0">
<Redirect SequenceName="Cat" InvokationDelay="AfterSequence" Value="1" />
<Redirect SequenceName="Dog" InvokationDelay="AfterSequence" Value="2" />
<Redirect SequenceName="GoldFish" InvokationDelay="AfterSequence" Value="3" />
</Item>
<Item FieldIndex="1" />
</Sequence>
<Sequence Name="Cat">
<Item FieldIndex="2" />
</Sequence>
<Sequence Name="Dog">
<Item FieldIndex="3" />
<Item FieldIndex="2" />
<Item FieldIndex="4">
<Redirect SequenceName="Training" InvokationDelay="AfterField" Value="True" />
</Item>
</Sequence>
<Sequence Name="GoldFish">
<Item FieldIndex="7" />
<Item FieldIndex="8" />
</Sequence>
<Sequence Name="Training">
<Item FieldIndex="5" />
<Item FieldIndex="6" />
</Sequence>
</FieldedText>`;
const meta = FtXmlMetaSerialization.deserialize(metaXml);
// Helper function to calculate ordinals for the current record
function calculateRecordOrdinals(reader: FtReader): number[] {
const maxFieldCount = 7;
const recOrdinals = new Array<number>(maxFieldCount);
// Read root sequence fields
const typeOrdinal = reader.getFieldIndexByName(TypeFieldName)!;
recOrdinals[0] = typeOrdinal;
recOrdinals[1] = reader.getFieldIndexByName(NameFieldName)!;
let fieldCount: number;
// Type field determines which sequence is active
const type = reader.fieldList.get(typeOrdinal).asBigInt;
if (type === CatType) {
recOrdinals[2] = reader.getFieldIndexByName(RunningSpeedFieldName)!;
fieldCount = 3;
} else if (type === DogType) {
recOrdinals[2] = reader.getFieldIndexByName(WalkDistanceFieldName)!;
recOrdinals[3] = reader.getFieldIndexByName(RunningSpeedFieldName)!;
recOrdinals[4] = reader.getFieldIndexByName(TrainingFieldName)!;
const training = reader.fieldList.get(recOrdinals[4]).asBoolean;
if (!training) {
fieldCount = 5;
} else {
recOrdinals[5] = reader.getFieldIndexByName(TrainerFieldName)!;
recOrdinals[6] = reader.getFieldIndexByName(SessionCostFieldName)!;
fieldCount = 7;
}
} else if (type === GoldFishType) {
recOrdinals[2] = reader.getFieldIndexByName(ColorFieldName)!;
recOrdinals[3] = reader.getFieldIndexByName(
ChineseClassificationFieldName,
)!;
fieldCount = 4;
} else {
fieldCount = 2;
}
return recOrdinals.slice(0, fieldCount);
}
// Create reader
const reader = new FtReader(meta, csvData);
console.log("Reading pets with sequences using ordinals (faster):\n");
// Track ordinals - reset when table changes
let recOrdinals: number[] | null = null;
do {
// Loop for each table in file
while (reader.read()) {
if (recOrdinals === null) {
// Ordinals only need to be calculated for first row of each table
// They remain valid for all records in the same table
recOrdinals = calculateRecordOrdinals(reader);
}
// Access fields by ordinal (faster than by name)
const values: unknown[] = [];
for (const ordinal of recOrdinals) {
values.push(reader.fieldList.get(ordinal).value);
}
console.log(
`${reader.recordCount},${reader.tableCount}: ${values.join(",")}`,
);
}
// Ordinals are no longer valid after table ends
recOrdinals = null;
} while (reader.nextResult());
console.log(
"\nNote: Using ordinals is faster than field names for large files",
);
console.log("because it avoids name lookup on every field access.");