Tree view control, get next available node name from the max value - c#

I'm working on a method that gives the next available node name if a node with the specified name exists. But the idea is that the method gives the next one from the max value, for example:
If I add a new node called "test", the method should return "test6" and not "test2". The same happens with the nodes that contain numbers as names:
If I add a new node with the name "20", the next available name should be "31".
So the idea is to get the "max value" from a sequence and add one to it. This tree node could contain easily more than 500 nodes, so is very important the optimization of this method. I've been trying to do this with this code, but does not work as expected:
internal static string GetNextAvailableName(TreeView treeView, string searchFor)
{
//Check if item exists
if (NodeExistsInSection(treeView, searchFor))
{
return searchFor;
}
else
{
//Initialize variables
string nextAvailableName = searchFor;
int counter = 0;
Match result = Regex.Match(searchFor, #"\d+$", RegexOptions.RightToLeft);
if (result.Success)
{
counter = int.Parse(result.Value);
if (searchFor.Length >= result.Value.Length)
{
searchFor = searchFor.Substring(0, (searchFor.Length - result.Value.Length));
}
}
while (SearchRecByText(treeView, nextAvailableName) != null)
{
counter++;
nextAvailableName = string.Join("", searchFor, counter);
}
return nextAvailableName;
}
}
internal static bool NodeExistsInSection(TreeView treeView, string searchFor)
{
bool nodeExists = false;
// Print each node recursively.
foreach (TreeNode n in treeView.Nodes)
{
//recursiveTotalNodes++;
if (LoopNodesRecursive(n, searchFor) != null)
{
nodeExists = true;
break;
}
}
return nodeExists;
}
internal static TreeNode SearchRecByText(TreeView treeView, string searchFor)
{
TreeNode matchedNode = null;
// Print each node recursively.
foreach (TreeNode n in treeView.Nodes)
{
//recursiveTotalNodes++;
matchedNode = LoopNodesRecursive(n, searchFor);
if (matchedNode != null)
{
break;
}
}
return matchedNode;
}
private static TreeNode LoopNodesRecursive(TreeNode treeNode, string searchFor)
{
// Visit each node recursively.
foreach (TreeNode tn in treeNode.Nodes)
{
if (tn.Text.Equals(searchFor, StringComparison.OrdinalIgnoreCase))
{
return tn;
}
}
return null;
}

If performance is your outright goal I think I'd have a dictionary track treenodes with a special case for if the user enters a number. From the code posted it seems that nodes must be uniquely named across the entire tree. As such I'd build a dictionary upon tree init and maintain it as I go
Here's the method that suggests new node names based on what the user types:
Dictionary<string, int> prefixes = new();
string GetNodeName(string prefix){
//strip trailing numbers entered by the user
prefix = Regex.Replace(prefix, "\\d+$", "");
//have we seen the prefix before?
var n = prefixes.GetValueOrDefault(prefix, 0);
prefixes[prefix] = n + 1;
if(n > 0) //nth time we saw it, return number suffix
return prefix+n;
if(prefix == "") //user entered just a number, for the first time
return "1";
return prefix; //user entered a new prefix
}
GetNodeName identifies the prefix text the user types by stripping off trailing numbers and checking what the next known number is for the resulting prefix. If the prefix is unknown it gets 0, 1 is then added as the next number and the 0 is special cased to "not having a numeric suffix"
If we need to restore the tree from somewhere we need to build up the max+1 values we see as we build:
void Build(string nodename){
//strip trailing numbers
var prefix = Regex.Replace(prefix, "\\d+$", "");
//get the number off the end. Uses c# 8 ranges; if your c# version is less, use Remove(length) instead
var number = int.Parse(nodename[prefix.Length..]);
//have we seen the prefix before?
var n = prefixes.GetValueOrDefault(prefix, 0);
//node number is higher than dictionary? Bump dict
if(number >= n)
prefixes[prefix] = number + 1;
}
Call build for every node text as you rebuild your tree from DB or whatever; it will bump up the number in the prefixes dictionary to be +1 of whatever it is seeing
I assume it's a logical error to allow a user to enter a prefix of "test2" repeatedly and you will make nodes of "test2","test21",test22" - I deem that the prefix is "test", the user supplied 2 is ignored and the node gets whatever is next in line for "test" ie "test7". This logic works if the user just enters a number, the prefix is then "" and is numbered accordingly

Related

How to parse string and prepare it for math

I want to parse latex string to math expression with following code
static string ParseString(List<string> startItems, string input)
{
foreach (var item in startItems)
{
int charLocation = input.IndexOf(item, StringComparison.Ordinal);
if (charLocation > 0)
{
switch (input.Substring(0, charLocation).Last())
{
case '{':
case '+':
case '-':
case '*':
break;
default:
input = input.Replace(item, "*" + item);
break;
}
}
}
return input;
}
static void Main(string[] args)
{
string ToParse = #"\sqrt{3}\sqrt{3}*4\sqrt{3}";
ToParse = ParseString(new System.Collections.Generic.List<string> { #"\frac", #"\sqrt" }, ToParse);
Console.WriteLine(ToParse);
}
I should get before every \sqrt word multiply char.But my program breaks in first \sqrt and cant parse others.
Result is
\sqrt{3}\sqrt{3}*4\sqrt{3}
And i want to get
\sqrt{3}*\sqrt{3}*4*\sqrt{3}
Sorry for bad English.
Based on provided example you can do as simple as input = input.Replace(item, "*" + item).TrimStart('*'); (Replace replaces all occurrences of string/char).
As for your code, you have next issues:
First inclusion of \sqrt returns charLocation set to 0 (arrays are xero-based in C#), so your check charLocation > 0 evaluates to false, so nothing happens
input.Substring(i, l) accepts 2 parameters - start index and length, so it is incorrect to use your charLocation as second parameter(even if you remove previous check, input.Substring(0, charLocation).Last() will end in exception Sequence contains no elements).
So if you want to go through all occurrences and insert symbol based on some logic then you'll need to have some cycle and move start index and calculate length(and also use something else, not Replace). Or you can dive into Regular Expressions.

How to get a direction on the same train line?

Can you help me with step by step logic that I need to get a direction on the same train line. with already having common train line with functions Next and Previous.
public IStation Next(IStation s)
{
if (!_stations.Contains(s))
{
throw new ArgumentException();
}
var index = _stations.IndexOf(s);
var isLast = index == _stations.Count -1;
if (isLast)
{
return null;
}
return _stations[index + 1];
}
public IStation Previous(IStation s)
{
if (!_stations.Contains(s))
{
throw new ArgumentException();
}
var index = _stations.IndexOf(s);
var isFirst = index == 0;
if (isFirst)
{
return null;
}
return _stations[index - 1];
}
And my function where I look for direction.
public string GetLineDirectiom(Station from, Station to, Line commonLine)
{
bool fromNextTo = true;
//to.Lines.Count();
//to.ToString();
var Final = commonLine.Next(from);
while (Final != null)
{
}
if (fromNextTo)
return "next";
else return "previous";
}
It looks like you are trying to "visit the stations along commonLine", starting at the from station.
The loop you have started is a valid start to that end; you need a variable to store the station you are currently visiting. Maybe the current variable name Final is a bit confusing to yourself here, because it is not the "final" station of the line, just the one you are currently visiting.
Therefore, let's name the variable currentStation. Then, you want to go to the next station until you have found to (and thereby know the direction), or until you have reached the end of the line:
var currentStation = from;
while (currentStation != null)
{
if (currentStation == to)
{
return "next";
}
currentStation = commonLine.Next(currentStation);
}
Now, this checks whether to is "ahead". If it wasn't, you can proceed to checking whether it can be found in the other direction, again starting at from:
currentStation = from;
while (currentStation != null)
{
if (currentStation == to)
{
return "previous";
}
currentStation = commonLine.Previous(currentStation);
}
If this loop doesn't find to either, apparently to is not on the line. Treat this case according to your preference.
Some remarks:
Indicating the direction as "next" or "previous" may be a bit misleading. If it really is the direction of the line, think about something like "forward" and "backward", as "next" and "previous" indeed imply the direct next/previous elements in a list.
While the above works, I do note that your Line object already has the stations in an indexed list. Therefore, a simpler way of achieving your goal might be to just determine the indices of the from and to stations on commonLine and compare which one is greater than the other.
It's not clear what you want to do and why you are returning string "next" and "prev" as a direction but in general to get the direction by the two stations:
public int GetStationIndex(IStation s)
{
var index = _stations.IndexOf(s);
if (index == -1)
{
throw new ArgumentException();
}
return index ;
}
public string GetLineDirection(Station from, Station to, Line commonLine)
{
var direction = commonLine.GetStationIndex(from)<commonLine.GetStationIndex(to)?"next" : "previous"
return direction;
}

How to determine if Binary Tree is BST

I am trying to figure out a logic to determine if Binary Tree is BST. I want to use the inorder approach and I don't want to use an extra array to store all incoming values as we know that Inorder should be in sorted order. I want to check the incoming value w/o having to store it in an array. Below is my attempt which is not working.
public bool CheckBST(BstNode root)
{
BstNode prev = new BstNode(Int32.MinValue);
if (root == null)
return true;
if (root.left != null)
{
return CheckBST(root.left);
}
if (prev != null && prev.data >= root.data) // means data is not sorted hence NOT BST
return false;
prev = root;
if(root.right!=null)
{
return CheckBST(root.right);
}
return true;
}
Given a binary tree, following determines if it is a valid binary search tree (BST).
The left subtree of a node contains only nodes with keys less than
the node's key.
The right subtree of a node contains only nodes with keys greater
than the node's key.
Both the left and right subtrees must also be binary search trees.
Let's see below example:
If you see the above Binary Tree is a BST.
Now let's see another example :
The root node's value is 5 but its right child's value is 4 which does not satisfy the condition mentioned above. So the given tree is not a BST.
Solution Code:
Given that the TreeNode is defined as
public class TreeNode
{
public int Val { get; set; }
public TreeNode Left { get; set; }
public TreeNode Right { get; set; }
public TreeNode(int x) { this.Val = x; }
}
The code to check the validation is
public bool IsValidBST(TreeNode root)
{
return IsValidBST(root, int.MinValue, int.MaxValue);
}
private bool IsValidBST(TreeNode root, int minValue, int maxValue)
{
if (root == null)
{
return true;
}
int nodeValue = root.Val;
if (nodeValue < minValue || nodeValue > maxValue)
{
return false;
}
return IsValidBST(root.Left, minValue, nodeValue - 1) && IsValidBST(root.Right, nodeValue + 1, maxValue);
}
Now the IsValidBST can be invoked with root node
bool isValidBST = IsValidBST(rootNode);
So usually in a BST there are three things in each node. That is the data, and the two pointers left and right. If there are more than two pointers available in any node then it is not a BST. It is probably best to determine at the level of the node if it there are more pointers than there should be. You would be wasting time and resources by searching the tree.
Here is a good way to go about doing it https://www.geeksforgeeks.org/a-program-to-check-if-a-binary-tree-is-bst-or-not/
You can't initilaze the prev everytime in CheckBST. You can make the prev global. Also I have made the prev as type integer.
int prev = Int32.MinValue; //made this global and integer type
public bool CheckBST(BstNode root) {
if (root == null)
return true;
bool isLeftBST = CheckBST(root.left);
if (isLeftBST == false) return false;
if (prev != Int32.MinValue && prev >= root.data) // means data is not sorted hence NOT BST
return false;
prev = root.data; //mark the prev before traversing the right subtree
return isLeftBST && CheckBST(root.right);
}
Ignore the syntax problems, if any. I tried more of a pseudo code.
Ofcourse there are other ways to solve this problem as well. Like keeping track of min and max value so far (in #user1672994 answer).
If you could make CheckBST return the range (min, max) of the BST being checked, then the following recursive function shall do:
// Defines the return value that represents BST check failure.
const pair<int, int> kCheckFailed(Int32.MaxValue, Int32.MinValue);
pair<int, int> CheckBST(const BstNode& curr)
{
pair<int, int> left_ret(curr.value, curr.value);
pair<int, int> right_ret(curr.value, curr.value);
// Makes sure the left subtree, if any, is a BST, and its max
// (`left_ret.second`) is no greater than `curr.value`
if (curr.left) {
left_ret = CheckBST(*curr.left);
if (left_ret == kCheckFailed || left_ret.second > curr.value)
return kCheckFailed;
}
// Makes sure the right subtree, if any, is a BST, and its min
// (`right_ret.first`) is not less than `curr.value`.
if (curr.right) {
right_ret = CheckBST(*curr.right);
if (right_ret == kCheckFailed || right_ret.first < curr.value)
return kCheckFailed;
}
// Returns range by combining min of left subtree and max of right subtree.
return make_pair(left_ret.first, right_ret.second);
}
Note that CheckBST takes a (sub)tree root by reference to ensure the node (curr) is always valid. However, curr.left or curr.right may still be NULL, in which cases, the corresponding min or max values, respectively, are just curr.value, as initialized to both ret_left and ret_right.
Recursive with time complexity of O(1).
Remove the commented our lines to see how it gets called.
For the first call pass isBST(root, null, null).
public bool isBST(Node root, Node l, Node r)
{
// Console.WriteLine($"Processing: isBST({root?.data}, {l?.data}, {r?.data})");
if (root == null) return true;
if (l != null && root.data <= l.data) return false;
if (r != null && root.data >= r.data) return false;
// Console.WriteLine($"isBST({root?.left?.data}, {l}, {root?.data}) && isBST({root?.right?.data}, {root?.data}, {r?.data})");
return isBST(root.left, l, root) && isBST(root.right, root, r);
}
You dont need prev.
Check recursively that max(left) is less than or equal root.
Check recursively that min(right) if greater than root.
Check if the left is BST.
Check if the right is BST.
Of course, check on nulls where needed.

How would I return the lowest value from the list without using shortcuts?

I was given a list of numbers, { 1, 2, 3, 4, 5, 6, 7 }. I was asked to return the lowest value without using shortcuts, e.g. .Min() etc.
I am not going to give you the answer to your homework question for you directly, however to get you started just loop over the list and keep track of the smallest one you found. When you are done the smallest you found is the smallest in the list.
For your second part, it is just basic problem solving. Look at the problem, break it into smaller pieces. For example, for your problem you could break it in to:
How do I loop over a list
How do I remember a value between loops
How do i compare if the remembered value is smaller than the current loop value
How do I replace my remembered value if the current loop value is smaller
Then solve each piece individually.
You can do this in old-fashion imperative way. Works with all comparable types:
public static class MyEnumerableExtensions
{
public static T Min<T>(this IEnumerable<T> list) where T : IComparable<T>
{
if (list == null)
{
throw new ArgumentNullException("list");
}
T min = default (T);
bool initialized = false;
foreach (T elem in list)
{
if (!initialized)
{
min = elem;
initialized = true;
}
else if (min == null) // Do not compare with null, reset min
{
min = elem;
}
else if (elem != null && min.CompareTo(elem) > 0) // Compare only when elem is not null
{
min = elem;
}
}
if (!initialized)
{
throw new InvalidOperationException("list is empty");
}
return min;
}
}
Usage:
var min1 = list.Min();
var min2 = MyEnumerableExtensions.Min(list);
Also, is only Min method is restricted from Linq, additional tricks are possible. Works for numbers only:
var minViaMax = -list.Select(x => -x).Max();
var minViaAggregate = list.Aggregate(Math.Min);

How can I programmatically select or scroll to a TreeNode in a TreeView based on a string path?

I am implementing one of the common scenarios of a TreeView control and I am using a drives, files and folders. to make a File System. That means each node potentially has a path.
Problem is, we have the ensureVisible method but am not entirely convinced this does what it says it does. There is no explicit 'setVisible' to false property. This meas by default all TreeNodes are going to be Visible !!!
Can someone come up with solution that proves it does work?
Heres a method stub am working with?
public void selectTreeNodeFromPath(string path)
{
//char[] delimiters = new char[]{ '\\' };
//string[] pathArray = path.Split(delimiters);
//int numberOfLvlsToProbe = pathArray.Length;
// foreach (TreeNode node in treeDrives.Nodes)
// {}
}
You can see that I have I started to attack this problem but I have hit a tumbling block after a simple test case yielded NO-EFFECT !!!
TreeNodes will be visible depending on the expanded condition of their parent nodes and where the scroll position of the control is. What the EnsureVisible method does is expand the proper parents and scroll the control to the specified node.
Assuming that your tree has already been populated with the nodes, you should be able to call EnsureVisible on the last node and the control will expand all the parents. Then you can set SelectedNode to have that node be selected.
Heres the solution:
WORKING and TESTED:
public void selectTreeNodeFromPath(string path)
{
// set up some delimters to split our path on.
char[] delimiters = new char[] { '\\' };
// split the array and store the values inside a string array.
string[] pathArray = path.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
// clean up this array.
ensurePathArrayAccuracy(ref pathArray);
// a simple celing variable.
int numberOfLvlsToProbe = pathArray.Length;
// a variable for to keep an eye on the level of the TreeNode.
int currentLevel = 0;
// this collection always get re-populated each iteration.
TreeNodeCollection globalTreeNCollection = treeDrives.Nodes;
do
{
// start iterating through the global tree node collection.
foreach (TreeNode rootNode in globalTreeNCollection)
{
// only set the level if the node level is less!
if (rootNode.Level < pathArray.Length)
{
currentLevel = rootNode.Level;
// the 'currentLevel' variable can also be used to help index the 'pathArray' to make comparisons straightforward with current node.
if (rootNode.Text == pathArray[currentLevel])
{
// update our control variables and start again, the next level down.
globalTreeNCollection = rootNode.Nodes;
// once we have found the node then ...
break;
}
}
else // this portion of code means we are at the end of the 'pathArray.'
{
treeDrives.SelectedNode = rootNode;
//treeDrives.SelectedNode.EnsureVisible();
// to make sure the loop ends ... we need to update the currentLevel counter
// to allow the loop to end.
currentLevel = numberOfLvlsToProbe;
break;
}
}
}
// if the 'currentLevel' is less than the 'numberOfLvlsToProbe' then we need
// to keep on looping till we get to the end.
while (currentLevel < numberOfLvlsToProbe);

Categories