UNPKG

read-gedcom

Version:
124 lines 4.92 kB
import { ErrorDuplicatePointer } from './error'; const PROGRESS_TRAVERSE_INTERVAL = 50000; const PROGRESS_ITERATE_INTERVAL = 5000; /** * Computes an index for each node in the tree. * This operation is idempotent: applying several times will not have further effects (but will cost resources as the whole tree will be traversed anyway). * @param rootNode The root node * @param noBackwardsReferencesIndex See {@link GedcomReadingOptions.noBackwardsReferencesIndex} * @param doHideIndex See {@link GedcomReadingOptions.doHideIndex} * @param progressCallback See {@link GedcomReadingOptions.progressCallback} * @category Gedcom parser */ export const indexTree = (rootNode, noBackwardsReferencesIndex = false, doHideIndex = false, progressCallback = null) => { if (progressCallback) { progressCallback(); } let i = 0; const stack = [[rootNode, 0]]; while (stack.length > 0) { const [node, childIndex] = stack[stack.length - 1]; // Peek if (childIndex < node.children.length) { stack.push([node.children[childIndex], 0]); // Push } else { // eslint-disable-next-line const [node, _] = stack.pop(); // Pop if (stack.length > 0) { stack[stack.length - 1][1]++; // Next child } indexNode(node, !doHideIndex); // Index if (stack.length === 0) { indexRecords(node); // Index records on root node } } i++; if (progressCallback && i % PROGRESS_TRAVERSE_INTERVAL === 0) { progressCallback(); } } if (!noBackwardsReferencesIndex) { indexBackwardsReferences(rootNode, progressCallback); } if (progressCallback) { progressCallback(); } }; const indexNode = (node, enumerable) => { const byTag = {}; node.children.forEach((child, idx) => { if (child.tag !== null) { if (byTag[child.tag] === undefined) { byTag[child.tag] = []; } byTag[child.tag].push(idx); } }); Object.defineProperty(node, '_index', { enumerable, configurable: true, writable: true, }); node._index = { byTag }; }; const indexRecords = (nodeRoot) => { const byTagPointer = {}; nodeRoot.children.forEach((child, idx) => { if (child.tag !== null) { if (child.pointer !== null) { if (byTagPointer[child.tag] === undefined) { byTagPointer[child.tag] = {}; } if (byTagPointer[child.tag][child.pointer] !== undefined) { throw new ErrorDuplicatePointer(`Duplicate pointer: ${child.pointer}`, child.indexSource + 1, nodeRoot.children[byTagPointer[child.tag][child.pointer]].indexSource, child.pointer); } byTagPointer[child.tag][child.pointer] = idx; } } }); // We assume that the index is defined nodeRoot._index.byTagPointer = byTagPointer; }; const indexBackwardsReferences = (rootNode, progressCallback = null) => { const get = (object, key, def) => { const value = object[key]; return value != null ? value : def; }; const index = rootNode._index; // We assume that the index is defined const families = get(index.byTagPointer, "FAM" /* Tag.Family */, {}); const asSpouse = {}, asChild = {}; Object.values(families).forEach((familyIdx, i) => { const family = rootNode.children[familyIdx]; const familyIndex = family._index; // Also safe, by assumption for (const spouseType of ["HUSB" /* Tag.Husband */, "WIFE" /* Tag.Wife */]) { for (const spouseIdx of get(familyIndex.byTag, spouseType, [])) { const spouseId = family.children[spouseIdx].value; if (spouseId !== null) { // We ignore the other possibility (even though it is a hard violation) if (asSpouse[spouseId] !== undefined) { asSpouse[spouseId].push(familyIdx); } else { asSpouse[spouseId] = [familyIdx]; } } } } for (const childIdx of get(familyIndex.byTag, "CHIL" /* Tag.Child */, [])) { const childId = family.children[childIdx].value; if (childId !== null) { // ditto if (asChild[childId] !== undefined) { asChild[childId].push(familyIdx); } else { asChild[childId] = [familyIdx]; } } } if (progressCallback && i % PROGRESS_ITERATE_INTERVAL === 0) { progressCallback(); } }); index.asSpouse = asSpouse; index.asChild = asChild; }; //# sourceMappingURL=indexer.js.map