Find maximum depth/level of a nested collection - c#

I want to create a Property which can find the depth of the nested tree structure. The below static finds out the depth/level by recursion. But is it possible to make this function as a property in the same class instead of a static method?
public static int GetDepth(MenuGroup contextMenuItems)
{
if (contextMenuItems == null || contextMenuItems.Items.Count == 0)
return 0;
var subMenu = contextMenuItems.Items.Select(b => b as MenuGroup);
if (!subMenu.Any())
return 1;
var subLevel = subMenu.Cast<MenuGroup>().Select(GetDepth);
return !subLevel.Any() ? 1 : subLevel.Max() + 1;
}
Some more info on the code:
MenuGroup and MenuItem are derived from MenuBase
MenuGroup has children nodes with ObservableCollection<MenuBase> Items as Child Elements
MenuItem is a leave node without any child.

Well you could easily turn it into an instance property, yes:
public int Depth
{
get
{
if (Items.Count == 0)
return 0;
var subMenu = Items.Select(b => b as MenuGroup);
if (!subMenu.Any())
return 1;
var subLevel = subMenu.Cast<MenuGroup>().Select(x = > x.Depth);
return !subLevel.Any() ? 1 : subLevel.Max() + 1;
}
}
That won't quite work yet due to the handling of non-MenuGroup items, but it can easily be fixed, using OfType instead of the Select and then Cast:
public int Depth
{
get
{
// Completely empty menu (not even any straight items). 0 depth.
if (Items.Count == 0)
{
return 0;
}
// We've either got items (which would give us a depth of 1) or
// items and groups, so find the maximum depth of any subgroups,
// and add 1.
return Items.OfType<MenuGroup>()
.Select(x => x.Depth)
.DefaultIfEmpty() // 0 if we have no subgroups
.Max() + 1;
}
}

public string GenerateMenu()
{
StringBuilder sb = new StringBuilder();
sb.Append("<nav id=\"nvMenu\" class=\"main-nav\"><ul>");
sb.Append(PrepareMenuUL(AppConfig._AppConfigInstance.Navigation.FirstOrDefault().NavigationClass));
sb.Append("</ul></nav>");
return sb.ToString();
}
private string PrepareMenuUL(List<Navigation> navigation)
{
StringBuilder sb = new StringBuilder();
if (Liflag == 1)
{
sb.Append("</li>");
Liflag = 0;
}
foreach (var item in navigation)
{
var subMenu = item.NavigationClass.Select(b => b as Navigation);
if (subMenu.Any())
{
sb.Append("<li class=\"dropdown\">");
if (subMenu.Any() && item.Url == "#")
sb.Append(string.Format("{1}<i class=\"icon-arrow\"></i>", BaseUrl + item.Url, item.Name));
else if (subMenu.Any() && item.Url != "#" && item.Url != null)
sb.Append(string.Format("{1}<i class=\"icon-rightarrow\"></i>", BaseUrl + item.Url, item.Name));
}
else
{
sb.Append("<li>");
sb.Append(string.Format("{1}", BaseUrl + item.Url, item.Name));
}
if (subMenu.Any())
sb.Append("<ul class=\"wd190\">");
if (item.NavigationClass.Count > 0)
{
Liflag = 1;
sb.Append(PrepareMenuUL(item.NavigationClass));
}
sb.Append("</li>");
if (subMenu.Any())
sb.Append("</ul>");
}
return sb.ToString();
}

Related

Selenium - wait until element is present, visible and interactable even on a scrollable modal in C#

Same question was asked for python here: Selenium - wait until element is present, visible and interactable. However the answers are not covering all the scenarios.
My implementation fails from time to time, when I am not in debug mode.
I consider that waiting for an ID is such a trivial task that should have a straight forward KISS implementation and should never fail.
public IWebElement WaitForId(string inputId, bool waitToInteract = false, int index = 0)
{
IWebElement uiElement = null;
while (uiElement == null)
{
var items = WebDriver.FindElements(By.Id(inputId));
var iteration = 0;
while (items == null || items.Count < (index + 1) ||
(waitToInteract && (!items[index].Enabled || !items[index].Displayed)))
{
Thread.Sleep(500);
items = WebDriver.FindElements(By.Id(inputId));
if (items.Count == 0 && iteration == 10)
{
// "still waiting...: " + inputId
}
if (iteration == 50)
{
// "WaitForId not found: " + inputId
Debugger.Break(); // if tried too many times, maybe you want to investigate way
}
iteration++;
}
uiElement = WebDriver.FindElement(By.Id(inputId));
}
return uiElement;
}
public IWebElement GetActiveElement(string id, int allowedTimeout = 20, int retryInterval = 250)
{
var wait = new DefaultWait<IWebDriver>(_driver)
{
Timeout = TimeSpan.FromSeconds(allowedTimeout),
PollingInterval = TimeSpan.FromMilliseconds(retryInterval)
};
return wait.Until(d =>
{
var element = d.FindElement(By.Id(id));
return element.Displayed || element.Enabled ? element : throw new NoSuchElementException();
});
// Usage would be something like this
GetActiveElement("foo").SendKeys("Bar");
You should be able to create extension method and call the extension instead
public static class WebDriverExtension
{
public static IWebElement GetActiveElement(this IWebDriver driver, string id, int allowedTimeout = 20, int retryInterval = 250)
{
var wait = new DefaultWait<IWebDriver>(driver)
{
Timeout = TimeSpan.FromSeconds(allowedTimeout),
PollingInterval = TimeSpan.FromMilliseconds(retryInterval)
};
return wait.Until(d =>
{
var element = d.FindElement(By.Id(id));
return element.Displayed || element.Enabled ? element : throw new NoSuchElementException();
});
}
}
// usage
_driver.GetActiveElement("foo").SendKeys("bar");

In C#, how can I order this list of objects by which item is greater?

I have a simple class called Team, that looks like this:
public class Team
{
public Team ParentTeam;
public string Name;
}
So it has a Name and a reference to another team that is its Parent Team.
I now have a list of Teams that I am getting back from a function
List<Team> list = GetTeamsList();
Given, a few assumptions:
All teams have a ParentTeam except one (the top team)
Every team returned in the list is part of the same hierarchy and its only a single hierarchy (no 2 teams at the same "level")
I now need to take the results of this function and order the list by the hierarchy
So imagine we have the following team information:
|| Team Name || Parent Team Name ||
||-----------||------------------||
|| Team A || Team B ||
|| Team B || Team C ||
|| Team C || Team D ||
|| Team D || null ||
but the GetTeamsList() function returns the teams in any random order. For example, it might come back list this:
var teamA = GetTeamA();
var teamB = GetTeamB();
var teamC = GetTeamC();
var teamD = GetTeamD();
List<Team> list = new List() { teamD, teamA, teamB, teamC };
where I need to reorder this list so it looks like this:
List<Team> list = new List() { teamA, teamB, teamC, teamD };
How could I reorder a list into the "correct" order based on the team hierarchy?
Several of the solutions given so far are correct, and all of them are at least quadratic in the number of teams; they will be inefficient as the number of teams grows large.
Here's a solution which is (1) linear, (2) shorter, and (3) easier to understand than some of the other solutions so far:
static IEnumerable<Team> SortTeams(IEnumerable<Team> teams)
{
var lookup = teams.ToDictionary(t => t.ParentTeam ?? new Team());
var current = teams.Single(t => t.ParentTeam == null);
do
yield return current;
while (lookup.TryGetValue(current, out current));
}
This produces the sequence in the reverse of the order you want, so put a Reverse on the end of the call if you want it in the other order:
Console.WriteLine(String.Join(" ", SortTeams(teams).Reverse().Select(t => t.Name)));
The "dummy" team is there because a dictionary does not allow a key to be null.
This is my suggestion:
public class Team
{
public Team ParentTeam;
public string Name;
int Level
{
get
{
int i = 0;
Team p = this.ParentTeam;
while (p != null)
{
i++;
p = p.ParentTeam;
}
return i;
}
}
static IEnumerable<Team> Sort(IEnumerable<Team> list)
{
return list.OrderBy(o => o.Level);
}
}
Of course, if there are Teams with equal level, you might use another criteria to sort them.
This should work:
static IEnumerable<Team> GetOrdered(IEnumerable<Team> teams)
{
var set = teams as HashSet<Team> ?? new HashSet<Team>(teams);
var current = teams.First(t => t.Parent == null);
while (set.Count > 1)
{
yield return current;
set.Remove(current);
current = set.First(t => t.Parent == current);
}
yield return set.Single();
}
This gives you the reversed order, so you should call Reverse() to get the order you are asking for.
We can find the ascendants of the null team, defining an extension
public static IEnumerable<Team> FindAscendants(this IEnumerable<Team> l, Team from)
{
Team t = l.FirstOrDefault(x =>
(x.ParentTeam?.Name ?? "").Equals(from?.Name ?? ""));
return new List<Team>() { t }.Concat(t != null ?
l.FindAscendants(t) : Enumerable.Empty<Team>());
}
and reverse the order of the null team's ascendants
list.FindAscendants(null).Reverse().Skip(1)
Edit
Alternative version of the extension with yield return
public static IEnumerable<Team> FindAscendants(this IEnumerable<Team> l, Team from)
{
Team t = l.FirstOrDefault(x =>
(x.ParentTeam?.Name ?? "").Equals(from?.Name ?? ""));
yield return t;
if (t != null)
foreach (Team r in l.FindAscendants(t))
{
yield return r;
}
}
Edit 2
In terms of the most efficient solution, a dictionary is the key.
As you can see now, there is no longer need to reverse the order.
So an optimized version would be
public static IEnumerable<Team> FindDescendandOptimized(this List<Team> l, Team from)
{
int count = l.Count;
var dic = l.ToDictionary(x => x.ParentTeam?.Name??"");
Team start = dic[from?.Name??""];
Team[] res = new Team[count];
res[count - 1] = start;
for (int i = count - 2; i >= 0; i--)
{
start = dic[start.Name];
res[i] = start;
}
return res;
}
with a test case and usage
List<Team> list = new List<Team>();
Team team = new Team();
team.Name = "0";
list.Add(team);
for (int i = 1; i < 200000; i++)
{
team = new Team();
team.Name = i.ToString();
team.ParentTeam = list.Last();
list.Add(team);
}
list.Reverse();
Console.WriteLine("Order List of " + list.Count +" teams");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
list.Shuffle();
Console.WriteLine("Shuffled List");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
DateTime start = DateTime.Now;
var res = list.FindDescendandOptimized(null);
list = res.ToList();
DateTime end = DateTime.Now;
Console.WriteLine("Reordered List");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
Console.WriteLine("Benchmark ms: " + (end - start).TotalMilliseconds);
Console.ReadLine();
where the test check is
static bool TestOrder(List<Team> list)
{
int tot = list.Count;
for (int i = 0; i < tot; i++)
{
if (!list[i].Name.Equals((tot-i-1).ToString()))
{
return false;
}
}
return true;
}
Edit 3
A final consideration, maybe obvious.
The absolutely most efficient way would have been to define a child team.
public class Team
{
public string Name;
public Team ParentTeam;
public Team ChildTeam;
}
appropriately filled like below
team.ParentTeam = list.Last();
list.Last().ChildTeam = team;
to enable an immediate reordering
DateTime start = DateTime.Now;
var res = list.OrderByChild(); //list.FindDescendandOptimized(null);
list = res.ToList();
DateTime end = DateTime.Now;
Console.WriteLine("Reordered List");
with a direct link
public static IEnumerable<Team> OrderByChild(this List<Team> l)
{
int count = l.Count;
Team start = l.First(x => x.ParentTeam == null);
Team[] res = new Team[count];
res[count - 1] = start;
for (int i = count - 2; i >= 0; i--)
{
start = start.ChildTeam;
res[i] = start;
}
return res;
}

Generic hashtable method doesn't work correctly in C#

I am trying to implement a hashtable in C#. Here's what I have so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GenericHashMap.hashtable
{
class GenericHashtable<T>
{
private List<Node<T>> array;
private int capacity;
public GenericHashtable(int capacity)
{
this.capacity = capacity;
array = new List<Node<T>>(capacity);
for (int i = 0; i < capacity; i++)
{
array.Insert(i, null);
}
}
public class Node<E>
{
private E info;
private Node<E> next;
public Node(E info, Node<E> next)
{
this.info = info;
this.next = next;
}
public E Info
{
get
{
return this.info;
}
set
{
this.info = value;
}
}
public Node<E> Next
{
get
{
return this.next;
}
set
{
this.next = value;
}
}
}
public bool IsEmpty()
{
if (array.Count == 0)
{
return true;
}
return false;
}
public void Add(T element)
{
int index = Math.Abs(element.GetHashCode() % capacity);
Node<T> ToAdd = new Node<T>(element, null);
Console.WriteLine("index = " + index);
if (array.ElementAt(index) == null)
{
array.Insert(index, ToAdd);
Console.WriteLine("The element " + array.ElementAt(index).Info.ToString() + " was found at index " + index);
}
else
{
Node<T> cursor = array.ElementAt(index);
while (cursor.Next != null)
{
cursor = cursor.Next;
}
if (cursor.Next == null)
{
cursor.Next = ToAdd;
Console.WriteLine("The element " + array.ElementAt(index).Info.ToString() + " was found at index " + index);
}
}
}
public bool Search(T key)
{
int index = Math.Abs(key.GetHashCode() % capacity);
Console.WriteLine("Index = " + index);
Console.WriteLine("The element " + array.ElementAt(index).Info.ToString());
if (array.ElementAt(index) == null)
{
return false;
}
else
{
if (array.ElementAt(index).Equals(key))
{
Console.WriteLine("The element " + key + "exists in the map at index " + index);
return true;
}
else
{
Node<T> cursor = array.ElementAt(index);
while (cursor != null && !(cursor.Info.Equals(key)))
{
cursor = cursor.Next;
}
if (cursor.Info.Equals(key))
{
Console.WriteLine("The " + key + "exists in the map");
return true;
}
}
}
return false;
}
public void PrintElements()
{
for (int i = 0; i < capacity; i++)
{
Node<T> cursor = array.ElementAt(i);
while (cursor != null)
{
Console.WriteLine(cursor.Info.ToString());
cursor = cursor.Next;
}
}
}
}
}
Now, I add the following strings in the table:
GenericHashtable<string> table = new GenericHashtable<string>(11);
table.Add("unu"); -> index 3
table.Add("doi"); -> index 0
table.Add("trei"); -> index 6
table.Add("patru"); -> index 7
table.Add("cinci"); -> index 2
table.Add("sase"); -> index 0
Now, everything's fine, the elements are added. But when I'm trying to search the element "unu" it is not found because it's index is not 3 anymore, it's 5. The index for "trei" is not 6, it's 7... I don't understand why the elements are changing their indexes. I suppose that something is wrong when the elements are added in the Add() method, but I can't figure out myself what. Any answers?
This is the problem, in your Add method:
array.Insert(index, ToAdd);
Your array variable isn't actually an array - it's a list. And you're inserting elements into it, rather than just setting the existing element. That will affect all the existing elements which have a later index - increasing their index.
I would suggest using an array instead, and just use the indexers:
private Node<T>[] nodes;
Then:
Node<T> node = nodes[index];
if (node == null)
{
nodes[index] = newNode;
}
...
(There are various other oddities about the code to be honest, but that's the reason for the current problem.)

how to iterate collection and change value

I know this is a dumb question because you cannot modify the loop collection while in a loop, but I do need to change it. I know I must not change the referenced objects, but I have no idea how to do this.
var orders = _orderService.GetOrders(o => !o.Deleted &&
o.OrderStatus != OrderStatus.Cancelled &&
o.OrderStatus != OrderStatus.Complete);
foreach (var order in orders)
{
if (order.PaymentStatus == PaymentStatus.Paid)
{
if (order.ShippingStatus == ShippingStatus.ShippingNotRequired || order.ShippingStatus == ShippingStatus.Delivered)
{
var tempOrder = _orderService.GetOrderById(order.Id);
SetOrderStatus(tempOrder , OrderStatus.Complete, true);
}
}
}
I always get an error.
UPDATED: I changed to this
var orders = _orderService.GetOrders(o => !o.Deleted &&
o.OrderStatus != OrderStatus.Cancelled && o.OrderStatus != OrderStatus.CompletE);
List<int> orderIndex = new List<int>();
orders.ToList().ForEach(x => orderIndex.Add(x.Id));
foreach(var index in orderIndex)
{
var order = _orderService.GetOrderById(index);
if (order.PaymentStatus == PaymentStatus.Paid)
{
if (order.ShippingStatus == ShippingStatus.ShippingNotRequired || order.ShippingStatus == ShippingStatus.Delivered)
{
SetOrderStatus(order, OrderStatus.Complete, true);
}
}
}
try
int count = orders.Count; // the length of the collect : may need a different method for different collection types.
for(int i = 0; i < count; i++)
{
var current = orders[i];
// do stuff with current.
}
use for loop instead of foreach loop
for(int i=0; i<orders.Count; i++)
{
if (orders[i].PaymentStatus == PaymentStatus.Paid)
{
if (orders[i].ShippingStatus == ShippingStatus.ShippingNotRequired || orders[i].ShippingStatus == ShippingStatus.Delivered)
{
var tempOrder = _orderService.GetOrderById(orders[i].Id);
SetOrderStatus(tempOrder , OrderStatus.Complete, true);
}
}
}

Calculate Preorder Tree Traversal From DataTable structure

I have a DataTable that has the following structure:
Root | Level 1 | Level 2 | Level 3 | Tree L | Tree R
Food 1 18
Fruit 2 11
Red 3 6
Cherry 4 5
Yellow 7 10
Banana 8 9
Meat 12 17
Beef 13 14
Pork 15 16
Using C#, I need to traverse this structure and calculate the correct Tree L and Tree R values for each node. This is just an example, the real structure has several hundred nodes that go out to at least Level 7, but possibly more.
Can anyone suggest how I might approach the code for calculating the left and right values?
So, you want to keep track of the visit order of the tree, but you have the order split into L and R. Those correspond to the "entrance" and "exit" of the Visit function for the Node structure. Assuming a Node class with a Node.Visit() method, you could:
private static List<Tuple<char,Node>> VisitOrder = new List<Tuple<char,Node>>();
public void Visit()
{
VisitOrder.Add(Tuple.Create('L', this));
// whatever visit logic you use here
VisitOrder.Add(Tuple.Create('R', this));
}
When you finish, all you have to do is do is look at the VisitOrder values. They're stored in the List in increasing order, where the index corresponds to it's position in the visit sequence. Each item in the List, then, is a Tuple describing which value it corresponds to and which Node it visited.
Edit:
To get the final output format, you could then do something like:
var LTree = VisitOrder
.Where(t => t.First == 'L')
.Select((t, i) => String.Format("{0}) {1}", i + 1, t.Second.Name));
var RTree = VisitOrder
.Where(t => t.First == 'R')
.Select((t, i) => String.Format("{0}) {1}", i + 1, t.Second.Name));
Here is how I figured it out:
private class FolderRow
{
public int Indent
{
get { return this.Row.GetValue("Indent", 0); }
set { this.Row["Indent"] = value; }
}
public int Left
{
get { return this.Row.GetValue("Tree L", 0); }
set { this.Row["Tree L"] = value; }
}
public int Right
{
get { return this.Row.GetValue("Tree R", 0); }
set { this.Row["Tree R"] = value; }
}
public Guid Id
{
get { return this.Row.GetValue("FolderID", Guid.Empty); }
}
public DataRow Row { get; private set; }
public int RowNum { get; set; }
public bool RowComplete { get { return this.Left > 0 && this.Right > 0; } }
public FolderRow(DataRow row, int rowNum)
{
this.Row = row;
this.RowNum = rowNum;
}
}
[TestMethod]
public void ImportFolderStructure()
{
var inputTable = FileUtil.GetDataSetFromExcelFile(new FileInfo(#"c:\SampleTree.xlsx")).Tables[0];
var currentLevel = 0;
var nodeCount = 1;
var currentRow = 0;
inputTable.Columns.Add("Indent", typeof (int));
inputTable.Columns.Add("Path", typeof (string));
var rightStack = new List<FolderRow>();
foreach (DataRow row in inputTable.Rows)
row["Indent"] = GetLevelPopulated(row);
foreach (DataRow row in inputTable.Rows)
{
if (row.GetValue("Indent", 0) == 0)
row["Tree L"] = 1;
}
while (true)
{
var row = GetRow(inputTable, currentRow);
if (row.Indent == 0)
{
currentRow++;
rightStack.Add(row);
continue;
}
// if the indent of this row is greater than the previous row ...
if (row.Indent > currentLevel)
{
currentLevel++;
nodeCount++;
row.Left = nodeCount;
// ... check the next row to see if it is further indented
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
} else if (row.Indent == currentLevel)
{
nodeCount++;
row.Left = nodeCount;
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
} else if (row.Indent < currentLevel)
{
currentLevel--;
nodeCount++;
row.Left = nodeCount;
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
}
if (inputTable.Rows.Cast<DataRow>().Select(r => new FolderRow(r, -1)).All(r => r.RowComplete))
break;
}
}
private int HandleNextRow(FolderRow row, int currentRow, List<FolderRow> rightStack, DataTable inputTable, ref int currentLevel,
ref int nodeCount)
{
var nextRow = GetRow(inputTable, currentRow + 1);
if (nextRow != null)
{
if (nextRow.Indent > row.Indent)
{
// ok the current row has a child so we will need to set the tree right of that, add to stack
rightStack.Add(row);
}
else if (nextRow.Indent == row.Indent)
{
nodeCount++;
row.Right = nodeCount;
}
else if (nextRow.Indent < row.Indent)
{
nodeCount++;
row.Right = nodeCount;
nodeCount++;
// here we need to get the most recently added row to rightStack, set the Right to the current nodeCount
var stackLast = rightStack.LastOrDefault();
if (stackLast != null)
{
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
}
currentLevel--;
}
currentRow++;
if (rightStack.Count > 1)
{
// before we move on to the next row, we need to check if there is more than one row still in right stack and
// the new current row's ident is less than the current branch (not current leaf)
var newCurrentRow = GetRow(inputTable, currentRow);
var stackLast = rightStack.LastOrDefault();
if (newCurrentRow.Indent == stackLast.Indent)
{
nodeCount++;
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
}
}
}
else
{
// reached the bottom
nodeCount++;
row.Right = nodeCount;
// loop through the remaining items in rightStack and set their right values
var stackLast = rightStack.LastOrDefault();
while (stackLast != null)
{
nodeCount++;
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
stackLast = rightStack.LastOrDefault();
}
}
return currentRow;
}
private FolderRow GetRow(DataTable inputTable, int rowCount)
{
if (rowCount >= inputTable.Rows.Count)
return null;
return rowCount < 0 ? null : new FolderRow(inputTable.Rows[rowCount],rowCount);
}
private int GetLevelPopulated(DataRow row)
{
var level = 0;
while (level < 14)
{
if (level == 0 && row["Root"] != DBNull.Value)
return level;
level++;
if (row["Level " + level] != DBNull.Value)
return level;
}
return -1;
}

Categories

Resources