sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
184 lines • 6.39 kB
JavaScript
export class Dag {
constructor() {
this.roots = new Array();
}
toDebugString() {
let result = "";
this.roots.forEach(root => {
result += root.toDebugString(0);
});
return result;
}
/**
* Creates a DAG from a map giving the relationship between a node and its parent, and a data map containing the payload
* for each ID.
* @param hierarchy The map containing the hierarchy information
* @param data The map containing the payload information
*/
initFromFlatList(hierarchy, data, disabled) {
this.roots = new Array();
// Create all nodes
const nodes = new Map();
for (const [id, parentIds] of hierarchy.entries()) {
if (!nodes.has(id)) {
nodes.set(id, new DagNode(id, data.get(id)));
}
if (parentIds !== null) {
parentIds.forEach(parentId => {
if (!nodes.has(parentId)) {
nodes.set(parentId, new DagNode(parentId, data.get(parentId)));
}
});
}
}
// set disabled nodes
for (const id of disabled) {
nodes.get(id).disabled = true;
}
// Establish parent-child relationships
for (const [id, parentIds] of hierarchy.entries()) {
const currentNode = nodes.get(id);
// no parents, it's a root
if (parentIds === null || parentIds.length == 0)
this.roots.push(currentNode);
else {
parentIds.forEach(parentId => {
const parentNode = nodes.get(parentId);
// addUnder and not moveUnder, otherwise the existing parent would be reset
currentNode.addUnder(parentNode);
});
}
}
}
/**
* A simple method to create a Dag from a map containing the hierarchy, and an array of items that are the payload
* and that have a getId() method to retrieve their ID
* @param hierarchy the map containing the hierarchy (id <-> list of parent ids)
* @param data the array containing the payload objects, each with a getId() function
*/
initFromIdableEntity(hierarchy, data) {
let dataMap = new Map();
data.forEach((item) => {
dataMap.set(item.getId(), item);
});
this.initFromFlatList(hierarchy, dataMap, new Array());
}
/**
* A simple method to create a Dag from only an array of items that are the payload
* and that have a getId() method to retrieve their ID, and a getParents() methods to retrieve their parents
* @param data the array containing the payload objects, each with a getId() function and a getParents() function
*/
initFromParentableAndIdAbleEntity(data, disabled) {
let dataMap = new Map();
data.forEach((item) => {
dataMap.set(item.getId(), item);
});
let hierarchyMap = new Map();
data.forEach((item) => {
hierarchyMap.set(item.getId(), item.getParents());
});
this.initFromFlatList(hierarchyMap, dataMap, disabled);
}
initFlatTreeFromFlatList(data) {
let dataMap = new Map();
data.forEach((item) => {
dataMap.set(item.getId(), item);
});
// empty hierarchy map
let hierarchyMap = new Map();
data.forEach((item) => {
hierarchyMap.set(item.getId(), []);
});
this.initFromFlatList(hierarchyMap, dataMap, new Array());
}
traverseBreadthFirst(processor) {
this.roots.forEach(root => {
processor(root);
});
this.roots.forEach(root => {
root.traverseBreadthFirst(processor);
});
}
traverseDepthFirst(processor) {
this.roots.forEach(root => {
processor(root);
root.traverseBreadthFirst(processor);
});
}
sort(compareFn) {
// when sorting the children array, sort their payload
this.roots.sort((a, b) => {
return compareFn(a.payload, b.payload);
});
// then sort recursively
this.roots.forEach(c => c.sort(compareFn));
}
}
export class DagNode {
constructor(id, payload) {
this.id = id;
this.payload = payload;
this.parents = new Array();
this.children = new Array();
}
/**
* Adds this node under a new parent. Existing parents are kept
* @param parent the new parent or null to set a root node
*/
addUnder(parent) {
// link with parent if a parent was given
if (parent) {
this.parents.push(parent);
parent.children.push(this);
}
}
/**
* Moves this node under a new parent. This will unlink the node from its existing parents
* @param parent the new parent or null to set a root node
*/
moveUnder(parent) {
// unlink from current parents
let theId = this.id;
this.parents.forEach(p => {
p.children = p.children.filter(n => n.id != theId);
});
// reset parents
this.parents = new Array();
// then add it under new parent
this.addUnder(parent);
}
sort(compareFn) {
// when sorting the children array, sort their payload
this.children.sort((a, b) => {
return compareFn(a.payload, b.payload);
});
// then sort recursively
this.children.forEach(c => c.sort(compareFn));
}
toDebugString(depth) {
let indent = "";
for (let i = 0; i < depth; i++) {
indent += " ";
}
let result = indent + "id:" + this.id + (this.disabled ? " !disabled" : "") + "\n";
this.children.forEach(child => {
result += child.toDebugString(depth + 2);
});
return result;
}
traverseBreadthFirst(processor) {
this.children.forEach(c => {
processor(c);
});
this.children.forEach(c => {
c.traverseBreadthFirst(processor);
});
}
traverseDepthFirst(processor) {
this.children.forEach(c => {
processor(c);
c.traverseBreadthFirst(processor);
});
}
}
//# sourceMappingURL=Dag.js.map