Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ordinal trees #22

Open
wants to merge 8 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/grove.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./trie/index";
export * from "./trie/index";
export * from "./ordinalTree/index";
117 changes: 117 additions & 0 deletions src/ordinalTree/OrdinalNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* Class representing a node in an OrdinalTree.
* Each node stores a character, its children, parent, and an associated pattern.
*/
class OrdinalNode {
/**
* Creates an instance of an OrdinalNode.
* @param {string} value - The value or character of the node.
*/
constructor(value) {
this.value = value; // The character stored in the node
this.children = []; // List of child nodes
this.parent = null; // Parent node of the current node
this.pattern = null; // Pattern associated with the node
}

/**
* Adds a child node to the current node, sorted according to a specified pattern.
* If no pattern is provided, the parent node's global pattern is used.
* @param {OrdinalNode} childNode - The child node to be added.
* @param {Array|null} pattern - The pattern to use for sorting the children.
*/
addChild(childNode, pattern = null) {
childNode.parent = this; // Set the current node as the parent of the child node

const finalPattern = pattern || this.parent?.globalPattern; // Determine the pattern to use
childNode.pattern = pattern; // Set the child's pattern
this.insertChild(childNode, finalPattern); // Insert the child node into the correct position
}

/**
* Inserts a child node into the current node's children, sorting based on a global pattern.
* If the pattern is not found, it falls back to alphabetical sorting.
* @param {OrdinalNode} child - The child node to be inserted.
* @param {Array} pattern - The pattern to use for sorting the children.
*/
insertChild(child, pattern) {
// Ensure pattern is not null or undefined, fallback to an empty array if necessary
pattern = pattern || [];

this.children.push(child); // Add the child to the children array

// Sort children based on the provided pattern
this.children.sort((a, b) => {
const aIndex = pattern.indexOf(a.value);
const bIndex = pattern.indexOf(b.value);

// If both characters are in the pattern, sort by their order in the pattern
if (aIndex !== -1 && bIndex !== -1) {
return aIndex - bIndex;
}
// If only one of the characters is in the pattern, give it higher priority
if (aIndex !== -1) return -1;
if (bIndex !== -1) return 1;

// If neither are in the pattern, compare alphabetically, with uppercase first
if (a.value.toLowerCase() === b.value.toLowerCase()) {
return a.value < b.value ? -1 : 1;
}
return a.value.toLowerCase() < b.value.toLowerCase() ? -1 : 1;
});
}

/**
* Prints the current node and its children in a tree-like structure.
* The nodes are printed with lines connecting parents to children and siblings.
* @param {string} indent - The indentation string for formatting the tree structure.
* @param {boolean} isLast - Flag indicating if the current node is the last child.
*/
printNode(indent = "", isLast = true) {
let treeLine = isLast ? "└── " : "├── ";

// Print the current node's value and associated pattern
if (this.value !== null && this.pattern !== null) {
console.log(`${indent}${treeLine}${this.value} (${this.pattern})`);
}

const childIndent = indent + (isLast ? " " : "│ "); // Adjust indent for sibling connections

// Recursively print all children with proper formatting
this.children.forEach((child, index) => {
child.printNode(childIndent, index === this.children.length - 1);
});
}

/**
* Gets the number of children this node has.
* @returns {number} - The number of children.
*/
getChildrenCount() {
return this.children.length;
}

/**
* Finds and returns a child node with a specific value.
* @param {string} value - The value of the child node to find.
* @returns {OrdinalNode|null} - The found child node, or null if no child with the specified value exists.
*/
findChild(value) {
return this.children.find((child) => child.value === value) || null;
}

/**
* Gets all the siblings of the current node.
* Siblings are all nodes sharing the same parent as the current node.
* @returns {OrdinalNode[]} - An array of sibling nodes.
*/
getSiblings() {
if (this.parent) {
return this.parent.children.filter((child) => child !== this); // Return all siblings except the current node
} else {
return []; // No siblings if there is no parent
}
}
}

export default OrdinalNode;
87 changes: 87 additions & 0 deletions src/ordinalTree/OrdinalTree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import OrdinalNode from "./OrdinalNode";

/**
* Class representing an ordinal tree structure.
* The tree is a hierarchical structure where each node represents a character,
* and edges between nodes represent the progression of the word.
*/
class OrdinalTree {
/**
* Creates an instance of an OrdinalTree.
* @param {Array|null} globalPattern - A global pattern to assign to nodes if no specific pattern is provided.
*/
constructor(globalPattern = null) {
this.root = new OrdinalNode("Root"); // Root node of the tree
this.globalPattern = globalPattern; // Global pattern to apply to the tree nodes
}

/**
* Adds a word to the tree, creating nodes for each character in the word.
* @param {string} word - The word to add to the tree.
* @param {Array|null} pattern - A specific pattern to associate with the nodes for the word.
*/
add(word, pattern = null) {
let currentNode = this.root; // Start from the root node
let i = 0;

// Traverse through the word
while (i < word.length) {
const char = word[i];
let childNode = currentNode.findChild(char); // Look for a child node with the character

// If a child node is found, concatenate the pattern if provided
if (childNode !== null) {
if (pattern !== null) {
childNode.pattern = childNode.pattern.concat(pattern);
}
}

// If no child node is found, create one and add it to the tree
if (!childNode) {
childNode = new OrdinalNode(char); // Create a new node for the character
currentNode.addChild(
childNode,
pattern || currentNode.pattern || this.globalPattern // Use provided pattern or fallback
);
}

currentNode = childNode; // Move to the next node
i++;
}
}

/**
* Prints the tree structure starting from the root node.
* This is a visual representation of the tree.
*/
printTree() {
this.root.printNode(""); // Call the printNode method of the root
}

/**
* Retrieves all the values from the tree by traversing it.
* @returns {Array} - An array containing all the node values.
*/
getAllValues() {
const values = [];
this.traverse((node) => values.push(node.value)); // Collect all node values during traversal
return values;
}

/**
* Traverses the tree and executes the provided callback for each node.
* @param {Function} callback - The function to execute for each node.
*/
traverse(callback) {
const traverseNode = (node) => {
callback(node); // Execute callback for the current node
node.children.forEach(traverseNode); // Traverse through all children recursively
};

if (this.root) {
traverseNode(this.root); // Start traversal from the root node
}
}
}

export default OrdinalTree;
4 changes: 4 additions & 0 deletions src/ordinalTree/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@


export { default as OrdinalTree } from "./OrdinalTree";
export { default as OrdinalNode } from "./OrdinalNode";
Loading