Populate XtraTreeList with dynamic nodes - c#

I have table with two columns:
+-------------+------------+
| Level | Desc |
+-------------+------------+
| 1 | a |
+-------------+------------+
| 2 | b |
+-------------+------------+
| 2 | c |
+-------------+------------+
| 1 | d |
+-------------+------------+
| 2 | e |
+-------------+------------+
| 2 | f |
+-------------+------------+
| 3 | g |
+-------------+------------+
| 1 | h |
+-------------+------------+
| 1 | i |
+-------------+------------+
| 2 | j |
+-------------+------------+
| 2 | k |
+-------------+------------+
And I need to create display of this data in XtraTreeview with two columns according to Level column and it should be like:
- 1 a
-- 2 b
-- 2 c
-1 d
-- 2 e
-- 2 f
-- 3 g
-1 h
-1 i
-- 2 j
-- 2 k
So, level columns represents the node. Level 1 is the main node, level 2 is subnode of level 1, level 3 is subnode of level 2, level 4 is subnode of 3...
I know how to populate Xtratreeview when there is fixed numbers of nodes and subnodes but in this case don't have idea how to populate where 1 node consist 3, 4 or more subnodes.
I've done this so far:
Populate TreeView:
DataTable table = new DataTable();
table.Columns.Add("Level");
table.Columns.Add("Data");
table.Rows.Add(1, "a");
table.Rows.Add(2, "b");
table.Rows.Add(2, "c");
table.Rows.Add(1, "d");
table.Rows.Add(2, "e");
table.Rows.Add(2, "f");
table.Rows.Add(3, "g");
table.Rows.Add(4, "z");
table.Rows.Add(5, "x");
table.Rows.Add(2, "h");
table.Rows.Add(3, "i");
table.Rows.Add(1, "j");
table.Rows.Add(2, "k");
TreeListNode rootNode = null;
for (int i = 0; i < table.Rows.Count; i++)
{
tl.BeginUnboundLoad();
TreeListNode parentForRootNodes = null;
if (table.Rows[i][0].ToString().Equals("1"))
{
rootNode = tl.AppendNode(new object[] { (string)table.Rows[i][1] }, parentForRootNodes);
}
if (table.Rows[i][0].ToString().Equals("2"))
{
tl.AppendNode(new object[] { (string)table.Rows[i][1] }, rootNode);
}
tl.EndUnboundLoad();
}
Create columns:
private void CreateColumns2(TreeList tl)
{
tl.BeginUpdate();
tl.Columns.Add();
tl.Columns[0].Caption = "Level";
tl.Columns[0].VisibleIndex = 0;
tl.Columns.Add();
tl.Columns[1].Caption = "Desc";
tl.Columns[1].VisibleIndex = 1;
tl.Columns.Add();
tl.EndUpdate();
}

Documentation you might like to read is here: https://documentation.devexpress.com/#windowsforms/CustomDocument198
You need at minimum three things for a tree:
Id
ParentId
Text
So the structure you've described needs to change to permit finding the parent for an item.
Once you have that, the concept goes like this:
Create an item for each node you want in the tree, I created my own class for this with the properties I wanted (Id, ParentId, Text...)
Then set the datasource of the tree control
Example:
var data = new List<TreeItem>
{
new TreeItem { Id = "L1_1", ParentId = "", Text = "ONE" },
new TreeItem { Id = "L1_2", ParentId = "", Text = "TWO" },
new TreeItem { Id = "L1_3", ParentId = "", Text = "THREE" },
new TreeItem { Id = "L2_1", ParentId = "L1_1", Text = "A" },
new TreeItem { Id = "L2_2", ParentId = "L1_1", Text = "B" },
new TreeItem { Id = "L2_3", ParentId = "L1_2", Text = "C" },
new TreeItem { Id = "L2_4", ParentId = "L1_2", Text = "D" },
new TreeItem { Id = "L2_5", ParentId = "L1_2", Text = "E" }
};
tree.Properties.DataSource = data;
}
class TreeItem
{
public string Id { get; set; }
public string ParentId { get; set; }
public string Text { get; set; }
}
The order of the items in the data source is irrelevant, what is important is the uniqueness of each id.
The above example produces a tree like this:
- ONE
-- A
-- B
- TWO
- THREE
-- C
-- D
-- E
I am doing this without my DevExpress installation and without a compiler, so please excuse any errors. however the concept remains the same.

Related

Get recurring child result by depth level

I have "googled" for many hour but still I am not able to find an answer. Basically, I have a table that looks like this:
Parent | ID
null | 1
1 | 2
2 | 3
3 | 4
3 | 5
3 | 6
4 | 7
null | 8
How can I use entity linq to filter based on Id and "depth level" (which is a simple count of elements including the Id element and n - 1 elements passed the Id element)?
For example when I pass Id 2 and depth level 2
Result will be
Parent | ID
2 | 3 //level 1
3 | 4 //level 2
3 | 5 //level 2
3 | 6 //level 2
If I pass Id 3 and depth level also 2
Result will be
Parent ID
3 | 4 //level 1
3 | 5 //level 1
3 | 6 //level 1
4 | 7 //level 2
Thanks for your help
I tired to simulate your scenario in a Console application. Following is one of the possible solution. Its very basic so you need to change and use it as you need. But it returns results as per logic you mention in your question.
class Program
{
static List<Data> data = new List<Data>();
static List<Data> result = new List<Data>();
static void Main(string[] args)
{
data.Add(new Data() { Parent = null, ID = 1 });
data.Add(new Data() { Parent = 1, ID = 2 });
data.Add(new Data() { Parent = 2, ID = 3 });
data.Add(new Data() { Parent = 3, ID = 4 });
data.Add(new Data() { Parent = 4, ID = 5 });
data.Add(new Data() { Parent = null, ID = 6 });
// Take() implementation is for Depth.
result = findChildren(3).Take(2).ToList();
Console.ReadLine();
}
static List<Data> findChildren(int Id)
{
return data.Where(x => x.Parent == Id ).Union(data.Where(x => x.Parent == Id).SelectMany(y => findChildren(y.ID))).ToList();
}
}
public class Data
{
public int? Parent { get; set; }
public int ID { get; set; }
}
var Id = 2;
var Level = 2;
var head = new Item { Id = Id };
var result = new List<Item>();
for(var i = 0; i < Level; i++)
{
head = context.Table.FirstOrDefault(x => x.ParetId == head.Id);
if(head != null)
result.Add(head);
else
break;
}

Generating a Tree Structure From Table

I have an excel document which is like below
+-------+-------+-------+
| Col1 | Col2 | Col3 |
+-------+-------+-------+
| item1 | | |
| | item2 | |
| | item3 | |
| | | item4 |
| | item5 | |
| item6 | | |
| | item7 | |
+-------+-------+-------+
In this table;
item1 is the parent of item2, item3, item5
item3 is the parent of item4
item6 is the parent of item7
I want to generate a tree structure from this table but I couldn't figure out how to do. How can I do this using C#.
Thanks
Keep a list of parents for all columns. The parent for the first column is the tree root. When you treat a row, append the item to the resecive parent. (It's an error when there is no such parent, foe example if item4 were in column 3.) Add the added item to the list and remove all lower items.
To illustrate:
current item col pnt parent list
| | | | [root]
| item1 | | | 1 0 [root, item1]
| | item2 | | 2 1 [root, item1, item2]
| | item3 | | 2 1 [root, item1, item3]
| | | item4 | 3 2 [root, item1, item3, item4]
| | item5 | | 2 1 [root, item1, item5]
| item6 | | | 1 0 [root, item6]
| | item7 | | 2 1 [root, item6, item7]
The parent of the current item is list[pnt], where the index of the parent element in the list is pnt = col - 1. The recently added element is always the last element in the list.
Recursive function CreateTree creates a tree starting from a given node as a root. I assumed that input data may be in a jagged array, where each array represents a column from your example table. The node is identified by a row in the column, and depth being a column from the columns jagged array.
internal class TreeNode<T> where T : class
{
public TreeNode(T payload)
{
Children = new List<TreeNode<T>>();
Payload = payload;
}
public List<TreeNode<T>> Children { get; }
public T Payload { get; }
}
public static TreeNode<T> CreateTree<T>(int row, int depth, T[][] columns) where T : class
{
var node = new TreeNode<T>(columns[depth][row]);
var maxDepth = columns.GetLength(0) - 1;
if (depth == maxDepth)
return new TreeNode<T>(columns[depth][row]);
var i = row + 1;
while (true)
{
if (i >= columns[depth].Length || columns[depth][i] != null)
break;
if (columns[depth + 1][i] != null)
{
var child = CreateTree(i, depth + 1, columns);
node.Children.Add(child);
}
i++;
}
return node;
}
Here is the usage based on your example. However, in the example the depth of the tree is 3, this solution works with variable length depth.
var depth = 3;
var columns = new string[depth][];
columns = new[]
{
new string[] {"item1", null, null, null, null, "item6", null},
new string[] {null, "item2", "item3", null, "item5", null, "item7"},
new string[] {null, null, null,"item4", null, null, null},
};
var topLevelColumn = columns[0];
var roots = new List<TreeNode<string>>();
for (int i = 0; i < topLevelColumn.Length; i++)
{
if (topLevelColumn[i] != null)
roots.Add(CreateTree(i, 0, columns));
}

What is the fastest way to compare two tables?

For example there are two tables with same schema, but different contents:
Table1
| field1 | field2 | field3 |
----------------------------------------
| 1 | aaaaa | 100 |
| 2 | bbbbb | 200 |
| 3 | ccccc | 300 |
| 4 | ddddd | 400 |
Table2
| field1 | field2 | field3 |
----------------------------------------
| 2 | xxxxx | 200 |
| 3 | ccccc | 999 |
| 4 | ddddd | 400 |
| 5 | eeeee | 500 |
The expected comparison result would be:
Deleted in B:
| 1 | aaaaa | 100 |
Mismatch:
Table1:| 2 | bbbbb | 200 |
Table2:| 2 | xxxxx | 200 |
Table1:| 3 | ccccc | 300 |
Table2:| 3 | ccccc | 999 |
Newly added in B
| 5 | eeeee | 500 |
Using C#, what is the fastest way to compare two tables?
Currently my implementation is:
Check if each row in table1 has an exact match in table2;
Check if each row in table2 has an exact match in table1.
The efficiency is n*n so for 100k rows it takes 20 mins to run on a server.
Many thanks
You can try something like this, should be quite fast:
class objType
{
public int Field1 { get; set; }
public string Field2 { get; set; }
public int Field3 { get; set; }
public bool AreEqual(object other)
{
var otherType = other as objType;
if (otherType == null)
return false;
return Field1 == otherType.Field1 && Field2 == otherType.Field2 && Field3 == otherType.Field3;
}
}
var tableOne = new objType[] {
new objType { Field1 = 1, Field2 = "aaaa", Field3 = 100 },
new objType { Field1 = 2, Field2 = "bbbb", Field3 = 200 },
new objType { Field1 = 3, Field2 = "cccc", Field3 = 300 },
new objType { Field1 = 4, Field2 = "dddd", Field3 = 400 }
};
var tableTwo = new objType[] {
new objType { Field1 = 2, Field2 = "xxxx", Field3 = 200 },
new objType { Field1 = 3, Field2 = "cccc", Field3 = 999 },
new objType { Field1 = 4, Field2 = "dddd", Field3 = 400 },
new objType { Field1 = 5, Field2 = "eeee", Field3 = 500 }
};
var originalIds = tableOne.ToDictionary(o => o.Field1, o => o);
var newIds = tableTwo.ToDictionary(o => o.Field1, o => o);
var deleted = new List<objType>();
var modified = new List<objType>();
foreach (var row in tableOne)
{
if(!newIds.ContainsKey(row.Field1))
deleted.Add(row);
else
{
var otherRow = newIds[row.Field1];
if (!otherRow.AreEqual(row))
{
modified.Add(row);
modified.Add(otherRow);
}
}
}
var added = tableTwo.Where(t => !originalIds.ContainsKey(t.Field1)).ToList();
Might be worth overriding Equals instead of AreEqual (or making AreEqual a helper method outside the class definition), but that depends on how your project is setup.

Compare 2 Datatables to find difference/accuracy between the columns

So, I have 2 separate datatables, that look pretty identical but the values in their rows might be different for instance.
EDIT:
I can have an unique ID BY creating a temporary identity column that can be used as primary key if that will make it easier. so think of ID column as the primary key than.
Table A
ID | Name | Value1 | Value2 | Value3
-------------------------------------
1 | Bob | 50 | 150 | 35
2 | Bill | 55 | 47 | 98
3 | Pat | 10 | 15 | 45
4 | Cat | 70 | 150 | 35
Table B
ID | Name | Value1 | Value2 | Value3
-------------------------------------
1 | Bob | 30 | 34 | 67
2 | Bill | 55 | 47 | 98
3 | Pat | 100 | 15 | 45
4 | Cat | 70 | 100 | 20
Output Should be:
Table C
ID | Name | TableAValue1 | TableBValue1 | DiffValue1 ....Samething for Value2 .....samething for value3
------------------------------------------------------
1 | Bob | 50 | 30 | 20
2 | Bill | 55 | 55 | 0
3 | Pat | 10 | 100 | 90
4 | Cat | 70 | 70 | 0
I Know the tedious method to do this is by using a forloop and looping through each row comparing column rows with each other. But I am not sure how to create a new Table C with the results I want. Also I think there might be a simpler solution using Linq which I am not very familiar with but I would be interested in the solution with linq if it faster and less lines of code. I am looking for the most optimal/efficient way of going about this. as these datatables can be anywhere between 5,000 to 15,000+ rows in size so memory usage becomes an issue.
LINQ is not faster, at least not in general. But it can help to increase readability.
You can use Enumerable.Join which might be more efficient than nested loops, but you need a loop to fill your third table anyway. So the first two columns are the identifiers and the rest are the values:
var query = from r1 in table1.AsEnumerable()
join r2 in table2.AsEnumerable()
on new { ID = r1.Field<int>("ID"), Name = r1.Field<string>("Name") }
equals new { ID = r2.Field<int>("ID"), Name = r2.Field<string>("Name") }
select new { r1, r2 };
var columnsToCompare = table1.Columns.Cast<DataColumn>().Skip(2);
foreach (var rowInfo in query)
{
var row = table3.Rows.Add();
row.SetField("ID", rowInfo.r1.Field<int>("ID"));
row.SetField("Name", rowInfo.r1.Field<int>("Name"));
foreach (DataColumn col in columnsToCompare)
{
int val1 = rowInfo.r1.Field<int>(col.ColumnName);
int val2 = rowInfo.r2.Field<int>(col.ColumnName);
int diff = (int)Math.Abs(val1-val2);
row.SetField(col.ColumnName, diff);
}
}
var tableC = new DataTable();
tableC.Columns.Add(new DataColumn("ID"));
tableC.Columns.Add(new DataColumn("Name"));
tableC.Columns.Add(new DataColumn("TableAValue1"));
tableC.Columns.Add(new DataColumn("TableBValue1"));
tableC.Columns.Add(new DataColumn("DiffValue1"));
foreach (DataRow rowA in tableA.Rows)
{
foreach (DataRow rowB in tableB.Rows)
{
if (Convert.ToInt32(rowA["ID"]) == Convert.ToInt32(rowB["ID"]) &&
rowA["Name"].ToString() == rowB["Name"].ToString() &&
Convert.ToInt32(rowA["Value1"]) != Convert.ToInt32(rowB["Value1"]))
{
var newRow = tableC.NewRow();
newRow["ID"] = rowA["ID"];
newRow["Name"] = rowA["Name"];
newRow["TableAValue1"] = rowA["Value1"];
newRow["TableBValue1"] = rowB["Value1"];
newRow["DiffValue1"] = Convert.ToInt32(rowA["Value1"]) - Convert.ToInt32(rowB["Value1"]);
tableC.Rows.Add(newRow);
}
}
}
Using LINQ, create an anonymous type as follows
var joinedRows = (from rowA in TableA.AsEnumerable()
from rowB in TableB.AsEnumerable()
where rowA.Field<String>("Name") == rowB.Field<String>("Name")
select new
{
ID = rowA.Field<int>("ID"),
Name = rowA.Field<String>("Name"),
TableAValue1 = rowA.Field<int>("Value1"),
TableBValue1 = rowB.Field<int>("Value1"),
DiffValue1 = Math.Abs(rowA.Field<int>("Value1") - rowB.Field<int>("Value1")),
TableAValue2 = rowA.Field<int>("Value2"),
TableBValue2 = rowB.Field<int>("Value2"),
DiffValue2 = Math.Abs(rowA.Field<int>("Value2") - rowB.Field<int>("Value2")),
TableAValue3 = rowA.Field<int>("Value3"),
TableBValue3 = rowB.Field<int>("Value3"),
DiffValue3 = Math.Abs(rowA.Field<int>("Value3") - rowB.Field<int>("Value3"))
});
Table.AsEnumerable() will give you an IEnumerable(of DataRow)
row.Field will cast it to the correct type for you
You can now use the anonymous type of joinedRows and create your new dataTable from it
This uses a strategy similar to kippermand's, but will probably perform slightly better on large sets of data by avoiding the O(n²) complexity of checking every ID against every other ID, and by reusing the values extracted from the data table:
// joining by row location
var joinedTableRows =
dt1.AsEnumerable().Zip(dt2.AsEnumerable(),
(r1, r2) => new{r1, r2});
// or, joining by ID
var joinedTableRows2 =
dt1.AsEnumerable().Join(dt2.AsEnumerable(),
r => r.Field<int>("ID"),
r => r.Field<int>("ID"),
(r1, r2) => new{r1, r2});
var result =
from row in joinedTableRows
let rowA = row.r1
let rowB = row.r2
let tableAValue1 = rowA.Field<int>("Value1")
let tableBValue1 = rowB.Field<int>("Value1")
let tableAValue2 = rowA.Field<int>("Value2")
let tableBValue2 = rowB.Field<int>("Value2")
let tableAValue3 = rowA.Field<int>("Value3")
let tableBValue3 = rowB.Field<int>("Value3")
select new
{
ID = row.r1.Field<int>("ID"),
Name = row.r1.Field<string>("Name"),
TableAValue1 = tableAValue1,
TableBValue1 = tableBValue1,
DiffValue1 = Math.Abs(tableAValue1 - tableBValue1),
TableAValue2 = tableAValue2,
TableBValue2 = tableBValue2,
DiffValue2 = Math.Abs(tableAValue2 - tableBValue2),
TableAValue3 = tableAValue3,
TableBValue3 = tableBValue3,
DiffValue3 = Math.Abs(tableAValue3 - tableBValue3)
};
Depending on how your data needs to be consumed, you could either declare a class matching this anonymous type, and consume that directly (which is what I'd prefer), or you can create a DataTable from these objects, if you have to.

join without forgetting data that wasn't affected by the join

I have three tables I'm interested in, here's the relevant information:
+---------------+ +---------------+ +---------------+ +---------------+
| Class | | Tests | | StudentClasses| | Student |
+---------------+ +---------------+ +---------------+ +---------------+
| ClassID | | ClassID | | ClassID | | StudentID |
| | | WholeYearTest | | StudentID | | StudentYear |
| | | TestYear | | | | |
+---------------+ +---------------+ +---------------+ +---------------+
I have a variable login, which is a Student.
I am running this query in LINQ:
from tests in App.db.Tests
join studentClasses in App.db.StudentClasses on tests.ClassID equals studentClasses.ClassID
where (
(tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
||
studentClasses.StudentID == login.StudentID
)
select tests;
Unfortunately, I'm only getting results which correspond to the condition studentClasses.StudentID == login.StudentID
What I want:
All tests that the student has to sit due to StudentYear being equal to TestYear and WholeYearTest being true
All tests that the student has to sit because one of the possibly multiple StudentClasses associated with them is listed under Tests.ClassID.
I think it's probably due to a blatant misunderstanding of how JOIN works, but I can't think of any other way to implement this. Can anyone put forward an implementation suggestion other than looping all tests? If not, I suppose I'll use a loop, but I'm sure there must be some way of implementing this in LINQ.
I think this will do what you want:
from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on new { tests.ClassID, login.StudentID }
equals new { studentClasses.ClassID, studentClasses.StudentID }
into gj
from subStudentClass in gj.DefaultIfEmpty()
where (
(tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
||
(subStudentClass != null && subStudentClass.StudentID == login.StudentID)
)
select tests
The biggest difference is that there is now an outer join so that you can still find results where the join fails.
Also, the join is now by both ClassID and StudentID, so that you won't get matches for other students.
With the following test data:
var App = new { db = new {
Tests = new[] {
new Test { ClassID = 1, WholeYearTest = true, TestYear = 1999 },
new Test { ClassID = 2, WholeYearTest = true, TestYear = 1999 },
new Test { ClassID = 3, WholeYearTest = false, TestYear = 1999 },
new Test { ClassID = 4, WholeYearTest = false, TestYear = 1999 },
},
StudentClasses = new[] {
new StudentClass { ClassID = 1, StudentID = 1 },
new StudentClass { ClassID = 1, StudentID = 2 },
new StudentClass { ClassID = 4, StudentID = 1 },
new StudentClass { ClassID = 3, StudentID = 2 },
}
} };
var login = new { StudentID = 1, StudentYear = 1999 };
Console.WriteLine(string.Join(Environment.NewLine, (
//above query
).Select(x => string.Join(",", x.ClassID, x.WholeYearTest, x.TestYear))));
It prints
1,True,1999
2,True,1999
4,False,1999
A single inner join is not sufficient; your query actually needs to be executed with an inner join AND a union.
Give this a try - I didn't test it out because I figure you can do that :)
Note - you may need to insert a Distinct() in there as well.
var q1 = (from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on tests.ClassID equals studentClasses.ClassID
select tests).Concat(App.db.Tests.Where(t=>t.WholeYearTest && t.TestYear == login.StudentYear));

Categories

Resources