Delimiter-separated string to TreeView C# - c#

I have an input stream something like this:
John
Peter
Vanesa
Vanesa.New
Josh
Josh.New
Josh.New.Under
...
I need to Add Nodes to TreeView Someting like this:
+Customers
+John
+Peter
+Vanesa
+New
+Josh
+New
+Under
...
I have an idea to split every string with parameter '.', but i have a problem with dynamicly loaded nodes. Maybe i have to use some kind of foreach...
I have old database table "group" with records id and GroupName. The are filled with these strings. I need to create some kind of "address" like: John.Element or Vanesa.New.Element or Josh.New.Under.Element, where Element is record from other datatable. The DB connection is not the problem, the problem is the dynamicly fill the tree
For now i have done adding strings that don't contains '.':
reader = readGroups.ExecuteNonQuery();
while(reader.Read())
{
string[] buff = reader.GetValue(1).ToString().Split('.');
if (buff.Length == 1)
{
treeView1.Nodes[0].Nodes.Add(reader.GetValue(1));
}
else
{
//group contains '.'
}
}
EDIT:
I have one more problem. There is records like this: John, John.New, John.Old, John.Older, John.Oldest ... So when the AddNodes() method runs, the foreach in the end of the method clears John.New, John.Old, John.Older nodes, but they got to go into the treenode John. If you have some idea...

For winforms this is what you will need. I'm using recursion to add each child node inside each parent node. And I've made changes so that it will create a list of unique nodes before it starts adding any to the actual treeview
internal class TreeNodeHierachy
{
public int Level { get; set; }
public TreeNode Node { get; set; }
public Guid Id { get; set; }
public Guid ParentId { get; set; }
public string RootText { get; set; }
}
private List<TreeNodeHierachy> overAllNodeList;
private void AddNodes(IEnumerable<string> data)
{
overAllNodeList = new List<TreeNodeHierachy>();
foreach (var item in data)
{
var nodeList = new List<TreeNodeHierachy>();
var split = item.Split('.');
for (var i = 0; i < split.Count(); i++)
{
var guid = Guid.NewGuid();
var parent = i == 0 ? null : nodeList.First(n => n.Level == i - 1);
var root = i == 0 ? null : nodeList.First(n => n.Level == 0);
nodeList.Add(new TreeNodeHierachy
{
Level = i,
Node = new TreeNode(split[i]) { Tag = guid },
Id = guid,
ParentId = parent != null ? parent.Id : Guid.Empty,
RootText = root != null ? root.RootText : split[i]
});
}
// figure out dups here
if (!overAllNodeList.Any())
{
overAllNodeList.AddRange(nodeList);
}
else
{
nodeList = nodeList.OrderBy(x => x.Level).ToList();
for (var i = 0; i < nodeList.Count; i++)
{
var existingNode = overAllNodeList.FirstOrDefault(
n => n.Node.Text == nodeList[i].Node.Text && n.Level == nodeList[i].Level && n.RootText == nodeList[i].RootText);
if (existingNode != null && (i + 1) < nodeList.Count)
{
nodeList[i + 1].ParentId = existingNode.Id;
}
else
{
overAllNodeList.Add(nodeList[i]);
}
}
}
}
foreach (var treeNodeHierachy in overAllNodeList.Where(x => x.Level == 0))
{
treeView1.Nodes.Add(AddChildNodes(treeNodeHierachy));
}
}
private TreeNode AddChildNodes(TreeNodeHierachy node)
{
var treeNode = node.Node;
foreach (var treeNodeHierachy in overAllNodeList.Where(n => n.ParentId == node.Id))
{
treeNode.Nodes.Add(AddChildNodes(treeNodeHierachy));
}
return treeNode;
}
/// <summary>
/// Handles the Click event of the button1 control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void button1_Click(object sender, EventArgs e)
{
//SearchActiveDirectoryWithCriteria("(mailnickname=TM418)");
var test = new List<string>
{
"John",
"Peter",
"Vanesa",
"Vanesa.New",
"Josh",
"Josh.New",
"Josh.New.Under",
"Josh.Old"
};
AddNodes(test);
}

This will probably do mainly what you want, you'll also need some xaml with a TreeView called treeView:
public TreeViewItem root;
public MainWindow()
{
InitializeComponent();
root = new TreeViewItem
{
Header = "Customers"
};
treeView.Items.Add(root);
addNode("John");
addNode("Peter");
addNode("Vanesa.New");
addNode("Josh");
addNode("Josh.New");
addNode("Josh.New.Under");
}
private void addNode(string values)
{
var n = root;
foreach (var val in values.Split('.'))
{
var isNew = true;
foreach (var existingNode in n.Items)
{
if (((TreeViewItem)existingNode).Header.ToString() == val)
{
n = (TreeViewItem)existingNode;
isNew = false;
}
}
if (isNew)
{
var newNode = new TreeViewItem
{
Header = val
};
n.Items.Add(newNode);
n = newNode;
}
}
}

I had the same problem. i solved that in this way:
defining a class that implement a tree:
class TreeBuilder
{
public int index,depth;
public string text;
public Dictionary<string,TreeBuilder> childs;
public void addToTreeVeiw(System.Windows.Forms.TreeNode root, TreeBuilder tb) {
foreach (string key in tb.childs.Keys) {
System.Windows.Forms.TreeNode t = root.Nodes.Add(tb.childs[key].text);
addToTreeVeiw(t, tb.childs[key]);
}
}
}
and the main part:
string[] lis = {"a","b","a.a","a.ab","c","cc.a","a.b.dd","samad.hah.hoh"};
treeView1.Nodes.Clear();
TreeBuilder Troot = new TreeBuilder();
TreeBuilder son;
Troot.depth = 0;
Troot.index = 0;
Troot.text = "root";
Troot.childs = new Dictionary<string, TreeBuilder>();
foreach (string str in lis)
{
string[] seperated = str.Split('.');
son = Troot;
int index= 0;
for (int depth = 0; depth < seperated.Length; depth++)
{
if (son.childs.ContainsKey(seperated[depth]))
{
son = son.childs[seperated[depth]];
}
else {
son.childs.Add(seperated[depth],new TreeBuilder());
son = son.childs[seperated[depth]];
son.index= ++index;
son.depth = depth+1;
son.text = seperated[depth];
son.childs = new Dictionary<string, TreeBuilder>();
}
}
}
treeView1.Nodes.Add("root");
Troot.addToTreeVeiw(treeView1.Nodes[0], Troot);
I guess result is what you want:

Re #charles380 solution:
I have updated the code to work with WPF .Net Core tree view.
public class TreeNodeHierarchy
{
public int Level { get; set; }
public TreeViewItem Node { get; set; }
public Guid Id { get; set; }
public Guid ParentId { get; set; }
public string RootText { get; set; }
}
public class TreeManager{
public List<TreeNodeHierarchy> overAllNodeList;
public void AddNodes(IEnumerable<string> data, ref TreeView treeView1)
{
overAllNodeList = new List<TreeNodeHierarchy>();
foreach (var item in data)
{
//\test
var nodeList = new List<TreeNodeHierarchy>();
var split = item.Split('.', StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < split.Count(); i++)
{
var guid = Guid.NewGuid();
var parent = i == 0 ? null : nodeList.First(n => n.Level == i - 1);
var root = i == 0 ? null : nodeList.First(n => n.Level == 0);
nodeList.Add(new TreeNodeHierarchy
{
Level = i,
Node = new TreeViewItem { Header = split[i], Tag = guid },
Id = guid,
ParentId = parent != null ? parent.Id : Guid.Empty,
RootText = root != null ? root.RootText : split[i]
});
}
// figure out dups here
if (!overAllNodeList.Any())
{
overAllNodeList.AddRange(nodeList);
}
else
{
nodeList = nodeList.OrderBy(x => x.Level).ToList();
for (var i = 0; i < nodeList.Count; i++)
{
var existingNode = overAllNodeList.FirstOrDefault(
n => n.Node.Header.ToString() == nodeList[i].Node.Header.ToString() && n.Level == nodeList[i].Level && n.RootText == nodeList[i].RootText);
if (existingNode != null && (i + 1) < nodeList.Count)
{
nodeList[i + 1].ParentId = existingNode.Id;
}
else
{
overAllNodeList.Add(nodeList[i]);
}
}
}
}
foreach (var treeNodeHierachy in overAllNodeList.Where(x => x.Level == 0))
{
treeView1.Items.Add(AddChildNodes(treeNodeHierachy));
}
}
private TreeViewItem AddChildNodes(TreeNodeHierarchy node)
{
var treeNode = node.Node;
foreach (var treeNodeHierachy in overAllNodeList.Where(n => n.ParentId == node.Id))
{
treeNode.Items.Add(AddChildNodes(treeNodeHierachy));
}
return treeNode;
}
}
}
Sample calling code:
var lstFolders= new List<string>
{
"John",
"Peter",
"Vanesa",
"Vanesa.New",
"Josh",
"Josh.New",
"Josh.New.Under",
"Josh.Old"
};
TreeManager treeManager = new TreeManager();
treeManager.AddNodes(lstFolders, ref tr);

Related

Recursively create a tree hierarchy off an array of strings

I have an array of strings separated by "!". I am trying to break that string up and create a tree hierarchy recursively in my custom class called PivotGroup. For example, what I am aiming at is to break up string array
string[] paths = new string[] {
"ROOT!ZZZ!AAA!EEE!15712",
"ROOT!ZZZ!AAA!EEE!15722",
"ROOT!ZZZ!AAA!EEE!13891"}
Into the PivotGroup class such as PivotGroup contains ChildGroups[] that embed the array strings.
So for example:
PivotGroup pgGroup = new PivotGroup();
pgGroup.ChildGroups[0] = PivotGroup[]; // Key:Book Level 3 Value: "AAA"
Now within Book Level 3 ChildGroups I need to set Book Level 4 which value is "EEE" and within the ChildGroups of "EEE" I would need to create another childGroup array which size in the case would be 3 called Book Level 5 and set another PivotGroup for each of following 15712, 15722, 13891
Here is my PivotGroup Class and embedded class Objects:
public class PivotGroup
{
public PivotGroup() { }
public PivotGroup(PivotGroupKey groupKey, PivotRow data, PivotGroup[] childGroups, bool leaf, int groupLevel)
{
GroupKey = groupKey;
Data = data;
ChildGroups = childGroups;
Leaf = leaf;
GroupLevel = groupLevel;
}
public PivotGroupKey GroupKey { get; private set; }
public PivotRow Data { get; private set; }
public PivotGroup[] ChildGroups { get; set; }
public bool Leaf { get; private set; }
public int GroupLevel { get; private set; }
public override string ToString()
{
return GroupKey + ", GroupLevel: " + GroupLevel + ", Children: " +
ChildGroups.Length + (Leaf ? " (Leaf)" : "");
}
}
public class PivotGroupKey
{
public PivotGroupKey()
{
}
public PivotGroupKey(string keyGroup, string keyValue)
{
if(keyGroup != null)
KeyGroup = string.Intern(keyGroup);
if (keyValue != null)
KeyValue = string.Intern(keyValue);
}
public string KeyGroup { get; private set; }
public string KeyValue { get; private set; }
public override string ToString()
{
return KeyGroup + ": " + KeyValue;
}
}
public class PivotRow
{
public PivotRow()
{
}
public PivotRow(string key, params object[] data) : this(key, true, data) { }
public PivotRow(string key, bool entitled, params object[] data)
{
Data = data;
Key = null;
Entitled = entitled;
}
public object[] Data { get; private set; }
public bool Entitled { get; private set; }
public string Key { get { return null; } set { } }
}
Main program I tried:
public class BookLevels
{
public string Root { get; set; }
public string BookLevel2 { get; set; }
public string BookLevel3 { get; set; }
public string BookLevel4 { get; set; }
public string BookLevel5 { get; set; }
}
class Program
{
static void BuildTree(string[] paths)
{
var BookPaths = paths.Select(x => x.Split('!'))
.Select(x => new BookLevels
{
Root = x[0],
BookLevel2 = x[1],
BookLevel3 = x[2],
BookLevel4 = x[3],
BookLevel5 = x[4]
}).GroupBy(z => new { z.BookLevel3, z.BookLevel4 }).ToArray();
var BookLevel3Cnt = BookPaths.Select(q => q.Key.BookLevel3).Count();
PivotGroup root = new PivotGroup(
new PivotGroupKey("Total", ""),
new PivotRow(null, new string[8]),
new PivotGroup[BookLevel3Cnt], false, 0);
foreach (var booklevel3 in BookPaths)
{
AddChildren(root, booklevel3);
}
}
private static void AddChildren(PivotGroup root, IGrouping<object, BookLevels> booklevel, int index = 0)
{
root.ChildGroups[index] = new PivotGroup(
new PivotGroupKey("Book Level " + (index + 3).ToString(), booklevel.Key.ToString()),
new PivotRow(null, new string[8]),
AddChildren(root, booklevel[index], index + 1), false, 0);
}
static void Main(string[] args)
{
string[] paths = new string[] {
"ROOT!ZZZ!AAA!EEE!15712",
"ROOT!ZZZ!AAA!EEE!15722",
"ROOT!ZZZ!AAA!EEE!13891",
"ROOT!ZZZ!AAA!DDD!15712",
"ROOT!ZZZ!AAA!DDD!15722",
"ROOT!ZZZ!AAA!DDD!13891",
"ROOT!ZZZ!BBB!DDD!15812",
"ROOT!ZZZ!BBB!DDD!15822",
"ROOT!ZZZ!BBB!DDD!13891",
};
BuildTree(paths);
Console.WriteLine();
Console.ReadLine();
}
I think my issue might be the way I am creating the Linq statement that breaks up the string, since I'm not sure how to progress thru it recursively.
I'm not sure what goes into which property. Also, for sake of simplicity and to be able to concentrate on the recursive algorithm, I redefine the group class like this (it does not mean that you have to change your class, instead, adapt my algorithm):
public class PivotGroup
{
public string Key { get; set; }
public List<PivotGroup> ChildGroups { get; } = new List<PivotGroup>();
public override string ToString() => Key; // Makes debugging easier.
}
The idea is that the values of the path go into the key. I made ChildGroups a list to be able to add children successively. My BuildTree returns the root
static PivotGroup BuildTree(string[] paths)
{
var root = new PivotGroup { Key = "ROOT" };
foreach (string path in paths) {
AddChildren(root, path.Split('!').Skip(1).ToList());
}
return root;
}
The recursive part goes into AddChildren. I convert the path into a List<string> to be able to remove the added part. AddChildren assumes that the first item in path is the first child to be added.
static void AddChildren(PivotGroup group, List<string> path)
{
string key = path[0];
int index = group.ChildGroups.FindIndex(g => g.Key == key);
PivotGroup child;
if (index >= 0) { // A child with this key exists.
child = group.ChildGroups[index]; // Select this existing child.
} else { // This key is missing. Add a new child.
child = new PivotGroup { Key = key };
group.ChildGroups.Add(child);
}
if (path.Count > 1) {
path.RemoveAt(0); // Remove the added child key and add the rest recursively.
AddChildren(child, path);
}
}
We add children by walking down the tree and adding new children if necessary.
This prints the tree recursively:
private static void PrintTree(PivotGroup group, int level)
{
Console.WriteLine(new String(' ', 2 * level) + group.Key);
foreach (PivotGroup child in group.ChildGroups) {
PrintTree(child, level + 1);
}
}
string[] paths = new string[] {
"ROOT!ZZZ!AAA!EEE!15712",
...
};
PivotGroup root = BuildTree(paths);
PrintTree(root, 0);
Console.ReadKey();
We could also use a loop instead of doing a recursion, since we add one branch at a time:
static PivotGroup BuildTree(string[] paths)
{
var root = new PivotGroup { Key = "ROOT" };
foreach (string path in paths) {
PivotGroup group = root;
string[] pathElements = path.Split('!');
for (int i = 1; i < pathElements.Length; i++) { // Element [0] is ROOT, we skip it.
string key = pathElements[i];
int index = group.ChildGroups.FindIndex(g => g.Key == key);
PivotGroup child;
if (index >= 0) { // A child with this key exists.
child = group.ChildGroups[index]; // Select this existing child.
} else { // This key is missing. Add a new child.
child = new PivotGroup { Key = key };
group.ChildGroups.Add(child);
}
group = child;
}
}
return root;
}
List<T>.FindIndex is inefficient for large lists. If you have large data sets and the order does not matter, switch to Dictionary<string, PivotGroup>. If you need the data to be sorted, use SortedDictionary<string, PivotGroup>.
Here is some simple recursive code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] paths = new string[] {
"ROOT!ZZZ!AAA!EEE!15712",
"ROOT!ZZZ!AAA!EEE!15722",
"ROOT!ZZZ!AAA!EEE!13891"};
List<List<string>> inputData = paths.Select(x => x.Split(new char[] {'!'}).ToList()).ToList();
Node root = new Node();
Node.ParseTree(root, inputData);
}
}
public class Node
{
public string name { get; set; }
public List<Node> children { get; set; }
public static void ParseTree(Node parent, List<List<string>> inputData)
{
parent.name = inputData.First().FirstOrDefault();
var groups = inputData.Select(x => x.Skip(1)).GroupBy(x => x.Take(1).FirstOrDefault());
foreach (var group in groups)
{
if (group.Key != null)
{
if (parent.children == null) parent.children = new List<Node>();
Node newNode = new Node();
parent.children.Add(newNode);
ParseTree(newNode, group.Select(x => x.Select(y => y).ToList()).ToList());
}
}
}
}
}

How to avoid out of memory using trie by C#

how to write billions of data into a trie with less memory
I want to extract some infomation from news like company names,so I write billions of company names into a trie,but it needs much memory and throw out of memory exception,I don't know how to solve it,so anyone can help,thanks in advance.
public class Node
{
public char Value { get; set; }
public List<Node> Children { get; set; }
public int Depth { get; set; }
public string Code { get; set; }
public bool Terminal { get; set; }
public Node(char value, int depth)
{
Value = value;
Depth = depth;
Children = new List<Node>();
}
public Node FindChildNode(char c)
{
foreach (var child in Children)
if (child.Value == c)
return child;
return null;
}
}
public class Trie
{
private Node _root;
public Trie()
{
_root = new Node('^',0);
}
public Node Prefix(string s)
{
var currentNode = _root;
var result = currentNode;
foreach (var c in s)
{
currentNode = currentNode.FindChildNode(c);
if (currentNode == null)
break;
result = currentNode;
}
return result;
}
public void Insert(string randomLength,string code)
{
var commonPrefix = Prefix(randomLength);
var current = commonPrefix;
for (var i = current.Depth; i < s.Length; i++)
{
var newNode = new Node(s[i], current.Depth + 1);
if (i+1==s.Length)
{
newNode.Terminal = true;
newNode.Code = code;
}
current.Children.Add(newNode);
current = newNode;
}
}
}
Trie t=new Trie();
t.Insert("C","ABCG00DFD");
The aboved statement run 1000000000 Loops and the "C" can be replaced with different string with different length,as the loops increasing,it throw out of memory exception,so how to avoid or change it?
Have a go at this Trie and see if you can get it to work for what you need:
public class Trie : Dictionary<char, Trie>
{
public void Add(string value)
{
var c = String.IsNullOrEmpty(value) ? '\0' : value[0];
if (!this.ContainsKey(c))
{
this[c] = new Trie();
}
if (c != '\0')
{
this[c].Add(value.Substring(1));
}
}
}

What is a cleaner or more efficient way of iterating over nested collections of objects

I'm working on building an object with nested hierarchy from a flattened list of BOM items (bill of materials) from SQL that contain the BOM level and the parent they belong to. Basically each parent that is an assembly of smaller sub parts needs to have its sub parts added to a collection within that object. I believe we have some parts that may iterate up to 10 levels deep, and I don't like the way this pattern is going.
This data set is from a stored procedure that runs a complicated CTE query, and I'm not interested in trying to make this work with Entity Framework, as we have a lot of databases in the mix, and we need better control over the SQL that we're writing (hence the stored procedure). Ultimately this will end up as JSON that I'll render as a collapsible tree in a browser.
Here's the ugly part that I'm hoping to clean up a bit. The gist of what's going on here before I begin iterating over the various levels is:
Establish the first item (incomplete, but this is the main item).
Execute a stored procedure (with parameter) to return a datatable.
Create a flattened list of the objects I want to nest.
Get the total number of nested levels that exist on the BOM.
Iterate over the various levels in a very ugly way.
public BOMItemModel GetItemBOM(string item)
{
try
{
var bom = new BOMItemModel { PLPartNumber = item, BOMLevel = 0 };
var par = new Hashtable();
par.Add("#Item", item);
var dt = db.GetDataTable("[part].[getItemBOM]", par);
var bomList = new List<BOMItemModel>();
foreach(DataRow r in dt.Rows)
{
bomList.Add(MapBomItem(r));
}
var bomLevels = bomList.Max(x => x.BOMLevel);
for(var i = 1; i < bomLevels; i++)
{
foreach (var b in bomList.Where(x => x.BOMLevel == i).ToList())
{
if (i == 1)
{
bom.SubItems.Add(b);
}
else
{
if (i == 2)
{
var lvl2Items = bom.SubItems;
var parent1 = lvl2Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent1 != null) parent1.SubItems.Add(b);
}
if (i == 3)
{
var lvl2Items = bom.SubItems;
foreach(var lvl2 in lvl2Items)
{
var lvl3Items = lvl2.SubItems;
var parent2 = lvl3Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent2 != null) parent2.SubItems.Add(b);
}
}
if (i == 4)
{
var lvl2Items = bom.SubItems;
foreach (var lvl2 in lvl2Items)
{
var lvl3Items = lvl2.SubItems;
foreach (var lvl3 in lvl3Items)
{
var lvl4Items = lvl3.SubItems;
var parent3 = lvl4Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent3 != null) parent3.SubItems.Add(b);
}
}
}
if (i == 5)
{
var lvl2Items = bom.SubItems;
foreach (var lvl2 in lvl2Items)
{
var lvl3Items = lvl2.SubItems;
foreach (var lvl3 in lvl3Items)
{
var lvl4Items = lvl3.SubItems;
foreach (var lvl4 in lvl4Items)
{
var lvl5Items = lvl4.SubItems;
var parent4 = lvl5Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent4 != null) parent4.SubItems.Add(b);
}
}
}
}
if (i == 6)
{
var lvl2Items = bom.SubItems;
foreach (var lvl2 in lvl2Items)
{
var lvl3Items = lvl2.SubItems;
foreach (var lvl3 in lvl3Items)
{
var lvl4Items = lvl3.SubItems;
foreach (var lvl4 in lvl4Items)
{
var lvl5Items = lvl4.SubItems;
foreach (var lvl5 in lvl5Items)
{
var lvl6Items = lvl5.SubItems;
var parent5 = lvl6Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent5 != null) parent5.SubItems.Add(b);
}
}
}
}
}
}
}
}
return bom;
}
catch (Exception ex)
{
Log.Error(ex.Message, ex);
}
return null;
}
This is what my model or class looks like:
public class BOMItemModel
{
public BOMItemModel()
{
SubItems = new List<BOMItemModel>();
}
public string ParentPLNumber { get; set; }
public string PartNumber { get; set; }
public string PartListName { get; set; }
public string ItemNumber { get; set; }
public string PLPartNumber { get; set; }
public string PartType { get; set; }
public string PLManufacturer { get; set; }
public string PLDescription { get; set; }
public decimal Qty { get; set; }
public int BOMLevel { get; set; }
public List<BOMItemModel> SubItems { get; set; }
}
The last property on this class is where I'm stuffing sub items of the parent item, and this can nest several levels deep.
Yes, the following would be much better:
for (var i = 1; i <= bomLevels; i++)
{
foreach (var b in bomList.Where(x => x.BOMLevel == i).ToList())
{
if (i == 1)
{
bom.SubItems.Add(b);
}
else
{
var parent = bomList.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber && b.BOMLevel - 1 == x.BOMLevel);
if (parent != null) parent.SubItems.Add(b);
}
}
}
Also, I would throw an error when parent is not found. For a more generic approach have a look at my other example: Converting table in tree

C# Compare two list and return value

I have a problem. I try compare two list currentItemsInColl and bPList. Inside bPList i have other list RequiredItems and now is what I need.
I want compare currentItemsInColl and RequiredItems and return bPList.craftingBlueprint.
I try Compare but I dont know how use it :/
using Devdog.InventoryPro;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CraftingAutoUpdate : MonoBehaviour {
public ItemCollectionBase itemCollection;
public ItemCollectionBase rewardCollection;
public CraftingCategory craftingCategory;
[Header("Blue Print List")]
public List<BlueprintList> bPList = new List<BlueprintList>();
public List<CurrentItemInCollList> currentItemsInColl = new List<CurrentItemInCollList>();
private CraftingBlueprint readyBlueprint;
public void OnShow()
{
GetBluePrint();
InvokeRepeating("StartUpdate",0f,0.05f);
}
public void OnHide()
{
CancelInvoke("StartUpdate");
}
private void StartUpdate()
{
UpdateDirectory();
UpdateFindMatchItems();
UpdateCraftResults();
}
private void GetBluePrint()
{
bPList.Clear();
foreach (var b in craftingCategory.blueprints)
{
if (b != null)
{
var rI = b.requiredItems;
var listReqItems = new List<RequiredItem>();
foreach (var e in rI)
{
listReqItems.Add(new RequiredItem(e.item.ID, e.amount));
}
bPList.Add(new BlueprintList(b.name, b, listReqItems));
}
}
}
private void UpdateDirectory()
{
currentItemsInColl.Clear();
foreach(var item in itemCollection)
{
if (item.item != null)
{
var cT = item.item.ID;
if (currentItemsInColl.Find(u =>u.itemID == cT) == null)
{
var itemCount = itemCollection.GetItemCount(item.item.ID);
currentItemsInColl.Add(new CurrentItemInCollList(item.item.ID, itemCount));
}
}
}
}
In this methode I try find same items in collections:
private void UpdateFindMatchItems()
{
readyBlueprint = null;
bool matchFailed = false;
int requiredItemCount = 0;
int currentItemsInCollCount = currentItemsInColl.Count;
foreach(var bp in bPList)
{
requiredItemCount = bp.RequiredItems.Count;
foreach(var rI in bp.RequiredItems)
{
if(CompareLists(currentItemsInColl, bp.RequiredItems))
{
print("aa");
}
print(currentItemsInCollCount);
}
}
private void UpdateCraftResults()
{
rewardCollection.Clear();
if (readyBlueprint != null)
{
foreach (var items in readyBlueprint.resultItems)
{
rewardCollection.AddItem(items.item,null,true,false);
}
}
}
I try somthing like this but is wont work with this lists:
public static bool CompareLists<T>(List<T> aListA, List<T> aListB)
{
if (aListA == null || aListB == null || aListA.Count != aListB.Count)
return false;
if (aListA.Count == 0)
return true;
Dictionary<T,T> lookUp = new Dictionary<T,T>();
// create index for the first list
for (int i = 0; i < aListA.Count; i++)
{
uint count = 0;
if (!lookUp.TryGetValue(aListA[i], out count))
{
lookUp.Add(aListA[i], 1);
continue;
}
lookUp[aListA[i]] = count + 1;
}
for (int i = 0; i < aListB.Count; i++)
{
uint count = 0;
if (!lookUp.TryGetValue(aListB[i], out count))
{
// early exit as the current value in B doesn't exist in the lookUp (and not in ListA)
return false;
}
count--;
if (count <= 0)
lookUp.Remove(aListB[i]);
else
lookUp[aListB[i]] = count;
}
// if there are remaining elements in the lookUp, that means ListA contains elements that do not exist in ListB
return lookUp.Count == 0;
}
}
And this is my lists:
/* LISTS */
[Serializable]
public class CurrentItemInCollList
{
public uint itemID;
public uint itemAmount;
public CurrentItemInCollList(uint newitemID, uint newItemAmount)
{
itemID = newitemID;
itemAmount = newItemAmount;
}
}
[Serializable]
public class BlueprintList
{
public string bluePrintName;
public CraftingBlueprint craftingBlueprint;
public List<RequiredItem> RequiredItems = new List<RequiredItem>();
public BlueprintList(string newBluePrintName, CraftingBlueprint newcraftingBlueprint, List<RequiredItem> list)
{
bluePrintName = newBluePrintName;
craftingBlueprint = newcraftingBlueprint;
RequiredItems = list;
}
}
[Serializable]
public class RequiredItem
{
public uint itemID;
public uint itemAmount;
public RequiredItem( uint newitemID, uint newItemAmount)
{
itemID = newitemID;
itemAmount = newItemAmount;
}
}
I forgot.. CurrentItemInCollList.itemAmount can be >= RequiredItems.itemAmount
Dictionary use hash values to compare objects.
The stored classes must implement public override int GetHashCode(){}
Use Linq - here is a small console example:
class Program
{
static void Main(string[] args)
{
//Required list
List<Order> currentItemsInColl = new List<Order>();
currentItemsInColl.Add(new Order() { Name = "bike1", Id = "01" });
currentItemsInColl.Add(new Order() { Name = "bike4", Id = "04" });
//List of all items
List<BPP> bPList = new List<BPP>();
bPList.Add(new BPP() { BikeName = "bike1", Idzzz = "01" });
bPList.Add(new BPP() { BikeName = "bike2", Idzzz = "02" });
bPList.Add(new BPP() { BikeName = "bike3", Idzzz = "03" });
bPList.Add(new BPP() { BikeName = "bike4", Idzzz = "04" });
bPList.Add(new BPP() { BikeName = "bike5", Idzzz = "05" });
//Blueprint List
List<BPP> Blueprint = new List<BPP>();
//get all items into the Blueprint list
foreach (Order i in currentItemsInColl)
{
List<BPP> tmp = bPList.FindAll(x => x.Idzzz.Contains(i.Id));
//here you add them all to a list
foreach (BPP item in tmp)
{
Blueprint.Add(item);
}
}
Console.ReadLine();
}
}
public class Order
{
public string Id { get; set; }
public string Name { get; set; }
}
public class BPP
{
public string Idzzz { get; set; }
public string BikeName { get; set; }
}
Sidenote: i am comparing the ID's in each of the lists! Hope it helps.

C# Parse IF-ELSE Statement

UPDATE
I don´t want you to do my Work and write code for me I just wanted a nurge in the right Direction!
So I have to be more specific with my Problem, give me a chance to do some work on this and I will update my question with the results ;-)
UPDATE 2
I´ve solved my Problem with Roslyn maybe not very elegant but it work for my needs, here is the code ;-)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
namespace ParserTest
{
public class MyParser
{
private int _currentLevel = 1;
public void TestMethod()
{
string testString =
#" if(#ISEMPTY(temp.tis_filterstatus2))
{
tis_datasheet_selection.is_selected = 'Y'
}
else
{
if(#ISEMPTY(temp.tis_programmtyp_filter)) { }
else
{
AND tis_programme_v.type = '#SUB(temp.tis_programmtyp_filter)'
}
if(#ISEMPTY(temp.tis_programmfilter)) { }
else
{
AND tis_programme_v.programm LIKE '#SUB(temp.tis_programmfilter)%'
}";
var result = this.Parse(testString);
var finalResult = this.GenerateDsl(result);
}
public List<IfStatement> Parse(string strToParse)
{
var result = new List<IfStatement>();
var syntaxTree = SyntaxTree.ParseText(#"using System;class C{static void M(){" + strToParse + "}}");
var rootNodes = syntaxTree.GetRoot().DescendantNodes().Where(getRootNodes);
result = rootNodes.Select(n => ToIfStatement(n, null)).ToList();
ApplyNestingLevel(result);
return result;
}
private string GenerateDsl(List<IfStatement> list)
{
var sb = new StringBuilder();
foreach(var ifStmt in list)
{
IfStatementToDsl(ifStmt, sb);
}
return sb.ToString();
}
private string IfStatementToDsl(IfStatement ifStmt, StringBuilder sb)
{
string sqr = "";
for (int i = 0; i < ifStmt.Level; i++)
{
sqr += "'";
}
sb.Append("#IF(");
sb.Append(ifStmt.Condition.ApplyLevel(ifStmt.Level) + "," + sqr);
sb.Append(ifStmt.Statement.ApplyLevel(ifStmt.Level));
if(ifStmt.Childs.Count > 0)
{
foreach(var c in ifStmt.Childs)
{
IfStatementToDsl(c, sb);
}
}
sb.Append(sqr + "," + sqr);
if(ifStmt.Else != null)
{
sb.Append(ifStmt.Else.Statement.ApplyLevel(ifStmt.Level));
foreach(var c in ifStmt.Else.Childs)
{
IfStatementToDsl(c, sb);
}
}
sb.Append(sqr + ")");
return sb.ToString();
}
#region Parsing-Methods
private IfStatement ToIfStatement(SyntaxNode node, SyntaxNode parent)
{
var ifNode = (IfStatementSyntax)node;
var ifStmt = new IfStatement
{
Condition = ifNode.Condition.ToString(),
Statement = GetIfStatement(ifNode),
Childs = GetIfChilds(ifNode)
};
if (ifNode.Else != null)
{
ifStmt.Else = new ElseStatement
{
Statement = GetElseStatement(ifNode.Else),
Childs = GetElseChilds(ifNode.Else)
};
}
return ifStmt;
}
private List<IfStatement> GetIfChilds(IfStatementSyntax node)
{
var childs = node.Statement.DescendantNodes().Where(n => WhereIfNodes(n, node));
return childs.Select(n => ToIfStatement(n, node)).ToList();
}
private List<IfStatement> GetElseChilds(ElseClauseSyntax node)
{
var childs = node.Statement.DescendantNodes().Where(n => WhereElseNodes(n, node));
return childs.Select(n => ToIfStatement(n, node)).ToList();
}
private string GetIfStatement(IfStatementSyntax node)
{
var result = node.Statement.DescendantNodes().Where(n => WhereIfStatement(n, node));
string returnValue = "";
foreach (var n in result)
{
returnValue += n.ToString();
}
return returnValue.CleanString();
}
private string GetElseStatement(ElseClauseSyntax node)
{
var result = node.Statement.DescendantNodes().Where(n => WhereElseStatement(n, node));
string returnValue = "";
foreach (var n in result)
{
returnValue += n.ToString() + " ";
}
return returnValue.CleanString();
}
private void ApplyNestingLevel(List<IfStatement> list)
{
foreach (var item in list)
{
item.Level = _currentLevel;
if (item.Childs.Count > 0 || (item.Else != null && item.Else.Childs.Count > 0))
{
_currentLevel++;
}
ApplyNestingLevel(item.Childs);
if (item.Else != null)
{
ApplyNestingLevel(item.Else.Childs);
}
}
}
#endregion
#region Linq Where-Conditions
private bool WhereIfNodes(SyntaxNode node, IfStatementSyntax parent)
{
if(node.Kind == SyntaxKind.IfStatement && (node.Parent.Parent == parent))
{
return true;
}
return false;
}
private bool WhereElseNodes(SyntaxNode node, ElseClauseSyntax parent)
{
if (node.Kind == SyntaxKind.IfStatement && (node.Parent.Parent == parent))
{
return true;
}
return false;
}
private bool WhereIfStatement(SyntaxNode node, IfStatementSyntax parent)
{
if ((node.Kind == SyntaxKind.ExpressionStatement || node.Kind == SyntaxKind.LocalDeclarationStatement)
&& (node.Parent.Parent == parent))
{
return true;
}
return false;
}
private bool WhereElseStatement(SyntaxNode node, ElseClauseSyntax parent)
{
if ((node.Kind == SyntaxKind.ExpressionStatement || node.Kind == SyntaxKind.LocalDeclarationStatement)
&& (node.Parent.Parent == parent))
{
return true;
}
return false;
}
private Func<SyntaxNode, bool> getRootNodes =
n => n.Kind == SyntaxKind.IfStatement &&
(n.Parent.Parent.Kind != SyntaxKind.ElseClause && n.Parent.Parent.Kind != SyntaxKind.IfStatement);
#endregion
}
public class IfStatement
{
public int Level { get; set; }
public string Condition { get; set; }
public string Statement { get; set; }
public ElseStatement Else { get; set; }
public List<IfStatement> Childs { get; set; }
}
public class ElseStatement
{
public string Statement { get; set; }
public List<IfStatement> Childs { get; set; }
}
public static class Ext
{
public static string CleanString(this string value)
{
return value.Replace("\t", "").Replace("\n", "").Replace("\r", "");
}
public static string ApplyLevel(this string value, int level)
{
int multiplier = level * 2;
if (level == 0)
multiplier = 1;
var sb = new StringBuilder(multiplier);
for (int i = 0; i < multiplier; i++)
{
sb.Append("'");
}
return value.Replace("'", sb.ToString());
}
}
}
I have to write if-else Statements in a Domain-Specific-Language which is really a pain in the ass!
(The DSL is from a Third-Party-Tool which I have to use to generate WHERE-Statements for SQL-Queries)
Syntax:
#IF(#ISEMPTY(#SUB(temp.last_name))),'if true','else')
#SUB() reads a textboxvalue
Sample:
#IF(#ISEMPTY(#SUB(temp.last_name))),'','account_contact.last_name = ''DOE'' ')
you have to double your single-quotes in the else statement and if you want to nest different "if-else" every time you go a level deeper you have to double the doubled single-quotes!
You see it´s not very simple to write if you have complicated contitions...
So I thought I write a parser that transforms a normal if-else statement to this DSL-Syntax!
The Parser should create objects of this class:
public class IfCondition
{
public int ID { get; set; }
public int ParentID { get; set; }
public int Level { get; set; }
public string Condition { get; set; }
public string Content { get; set; }
public string ElseContent { get; set; }
}
based on a Collection of this Objects I could generate the DSL-Statements!
So my Problem is I really don´t have a clue, how to parse a String like this:
IF(#ISEMPTY(#SUB(temp.last_name))) { }
ELSE
{
IF(#SUB(temp.test) = 'Y')
{
account_contact.last_name = 'DOE'
}
ELSE
{
account_contat.first_name = "JOHN"
}
}
can somebody give me a nudge in the right direction?
If you use Roslyn for syntax rewritting. Not to just write about what you can do, here is an easy example.
Basically how it works. You create a syntax tree for your code and then use predefined visitors to visit those nodes you want to rewrite, you replace them by valid C# code and then you can compile this tree and make it work.
EDIT:
Another, more reliable source with an example

Categories