I'm trying to build an XML tree of some data with a parent child relationship, but in the same table.
The two fields of importance are
CompetitionID
ParentCompetitionID
Some data might be
CompetitionID=1,
ParentCompetitionID=null
CompetitionID=2,
ParentCompetitionID=1
CompetitionID=3,
ParentCompetitionID=1
The broken query I have simply displays results in a flat format. Seeing that I'm working with XML, some sort of recursive functionality is required. I can do this using normal for loop recursion, but would like to see the linq version. Any help appreciated.
var results =
from c1 in comps
select new {
c.CompetitionID,
SubComps=
from sc in comps.Where (c2 => c2.CompetitionID == c1.CompetitionID)
select sc
};
Update
I found an interesting article by Chris Eargle here that shows you how to call lambda delegates recursively. Here is the code. Thanks Chris!
Func<int, int> factoral = x => x <= 1 ? 1 : x + factoral(--x);
Func<int, int> factoral = null;
factoral = x => x <= 1 ? 1 : x + factoral(--x);
^ added code formatting to show the lamba funcs
The trick is to assign null to the Func delegate first.
Don't know how to write a recursive LINQ. But I think no recursion is actually required here. A tree may be built in just two steps:
Dictionary<int, Competition> dic = comps.ToDictionary(e => e.CompetitionID);
foreach (var c in comps)
if (dic.ContainsKey(c.ParentCompetitionID))
dic[c.ParentCompetitionID].Children.Add(c);
var root = dic[1];
The root variable now contains the complete tree.
Here's a complete sample to test:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
class Competition
{
public int CompetitionID;
public int ParentCompetitionID;
public List<Competition> Children=new List<Competition>();
public Competition(int id, int parent_id)
{
CompetitionID = id;
ParentCompetitionID = parent_id;
}
}
class Program
{
static void Main(string[] args)
{
List<Competition> comps = new List<Competition>()
{
new Competition(1, 0),
new Competition(2,1),
new Competition(3,1),
new Competition(4,2),
new Competition(5,3)
};
Dictionary<int, Competition> dic = comps.ToDictionary(e => e.CompetitionID);
foreach (var c in comps)
if (dic.ContainsKey(c.ParentCompetitionID))
dic[c.ParentCompetitionID].Children.Add(c);
var root = dic[1];
}
}
}
I know I'm a little too late here. But you said you already had a version using foreach :) So if it should actually be recursive and use linq this would be a solution:
internal class Competition
{
public int CompetitionID;
public int ParentCompetitionID;
public Competition(int id, int parentId)
{
CompetitionID = id;
ParentCompetitionID = parentId;
}
}
internal class Node
{
public Node(int id, IEnumerable<Node> children)
{
Children = children;
Id = id;
}
public IEnumerable<Node> Children { get; private set; }
public int Id { get; private set; }
}
internal class Program
{
static void Main(string[] args)
{
var comps = new List<Competition>
{
new Competition(1, 0),
new Competition(2, 1),
new Competition(3, 1),
new Competition(4, 2),
new Competition(5, 3)
};
Node root = ToTree(0, comps);
}
static readonly Func<int, IEnumerable<Competition>, Node> ToTree =
(nodeId, competitions) => new Node(nodeId, from c in competitions where c.ParentCompetitionID == nodeId select ToTree(c.CompetitionID, competitions));
}
You can get a tree like structure, combining LINQ and recursion with delegates. In this example I use a XML structure like this:
<Competitions>
<Competition ID="1" />
<Competition ID="2" ParentCompetitionID="1" />
<Competition ID="3" ParentCompetitionID="1" />
<Competition ID="4" />
</Competitions>
So to store node data in code and facilitate navigation, create a class like this:
class Competition
{
public int CompetitionID { get; set; }
public IEnumerable<Competition> Childs { get; set; }
}
Now using Linq to XML you load the xml file into an XDocument. After that declare a delegate that iterates over all the xml elements inside the document selecting nodes that have an id mathing the delegate's id paremeter. When selecting each node, it calls to the delegate again, passing the id of the parent node to look for. It first starts with the id parameter set to null, so, selecting firts the root nodes:
var doc = XDocument.Load("tree.xml");
//Declare the delegate for using it recursively
Func<int?, IEnumerable<Competition>> selectCompetitions = null;
selectCompetitions = (int? id) =>
{
return doc.Elements("Competitions").Elements().Where(c =>
{
//If id is null return only root nodes (without ParentCompetitionID attribute)
if (id == null)
return c.Attribute("ParentCompetitionID") == null;
else
//If id has value, look for nodes with that parent id
return c.Attribute("ParentCompetitionID") != null &&
c.Attribute("ParentCompetitionID").Value == id.Value.ToString();
}).Select(x => new Competition()
{
CompetitionID = Convert.ToInt32(x.Attribute("ID").Value),
//Always look for childs with this node id, call again to this
//delegate with the corresponding ID
Childs = selectCompetitions(Convert.ToInt32(x.Attribute("ID").Value))
});
};
var competitions = selectCompetitions(null);
To test it you can do a simply recurring method that prints the tree to the console:
private static void Write(IEnumerable<Competition> competitions, int indent)
{
foreach (var c in competitions)
{
string line = String.Empty;
for (int i = 0; i < indent; i++)
{
line += "\t";
}
line += "CompetitionID = " + c.CompetitionID.ToString();
Console.WriteLine(line);
if (c.Childs != null && c.Childs.Count() > 0)
{
int id = indent + 1;
Write(c.Childs, id);
}
}
}
Hope it helps!
I've done something very similar using LINQ's group by
I don't use the query syntax of LINQ, so forgive me if this is wrong:
var results = from c in comps
group c by c.ParentCompetitionID into g
select new { ParentId = g.Key, ChildId = g };
Of course, this would be better if your classes looked something like:
class Competition {
int Id;
string Description;
Competition ParentCompetition;
}
Then, instead of grouping by ID only, you can group by the entire competition, which makes generating the XML faster and easier.
var results = from c in comps
group c by c.ParentCompetition into g
select new { Parent = g.Key, Child = g };
class Competition
{
int ID { get; set;}
int ParentID { get; set; }
IEnumerable<Competition> Children { get; set; }
}
public IEnumerable<Competition> GetChildren(
IEnumerable<Competition> competitions, int parentID)
{
IEnumerable<Competition> children =
competitions.Where(c => c.ParentID == parentID);
if (children.Count() == 0)
return null;
return children.Select(
c => new Competition { ID = c.ID, Children = GetChildren(c.ID) };
}
Then you can just call GetChildren, passing the ID of the root as the parentID, and that will return a tree structure. You can also change the Competition object to the XML API of your choice.
I know this isn't exactly what you're looking for, but afaik LINQ doesn't support recursion. Nevertheless, the LIN part of LINQ means language integrated, which is exactly what I used.
While you can not do this with a single query (unless you are calling SQL directly with a CTE), you can limit the number of queries to the depth of the tree.
The code is too long to paste, but the basic steps are:
Collect root nodes and add to 'all' nodes
Collect nodes with parent in 'all' nodes (pass list to query)
Add nodes in step 2 to 'all' nodes
Repeat 2 - 3 till step 2 returns 0 nodes (which should be depth of tree + 1, I think).
You can minimize the amount of nodes passed to the query in step 2. SQL server tends to bomb out with a list of more than 2000 entries. (SQL Compact has no such issue though).
Related
I have to create an indented navigation menu using below data from a .csv file:
ID;MenuName;ParentID;isHidden;LinkURL1;Company;NULL;False;/company2;About Us;1;False;/company/aboutus3;Mission;1;False;/company/mission4;Team;2;False;/company/aboutus/team5;Client 2;10;False;/references/client26;Client 1;10;False;/references/client17;Client 4;10;True;/references/client48;Client 5;10;True;/references/client510;References;NULL;False;/references
Using this data I have to develop an application that will parse the file and present the content in a console as the example below:
. Company.... About Us....... Team.... Mission. References.... Client 1.... Client 2
Menu items should be indented (depending on the parent), hidden items (isHidden==true) shouldn't be presented and items should be ordered alphabetically. So far I tried:
using (StreamReader sr = new StreamReader(#"file.csv"))
{
// Read the stream to a string, and write the string to the console.
string [] lines = sr.ReadToEnd().Split(/*';', */'\n');
for (int i = 1; i < lines.Length; i++)
{
Console.WriteLine($"String no {i} is : {lines[i-1]}");
}
}
With this i'm getting the lines but I'm stuck after that. I'm new in coding so any help will be appreciated :)
heres some code that should help you get off.
Working sample:
https://dotnetfiddle.net/L37Gjr
It first parses the data to a seperate object. This then gets used to build a m-ary tree, or a hierachical structure of connected nodes. (a node has a reference to 0 or more children).
https://en.wikipedia.org/wiki/M-ary_tree
Then tree traversal (use google if you need to know more) is used to insert and print the output, There is still something wrong however. it now uses level order traversal to print, this however comes up with an error:
Found root:1 - Company
Found root:10 - References
-------------------
1 - Company
2 - About Us
3 - Mission
4 - Team
10 - References
6 - Client 1
5 - Client 2
As you can see, it prints 4 - Team on the wrong level. I'll leave it to you to fix it (because i ran out of time), and if not i hope i gave you plenty ideas to go off and research on your own.
// sample for https://stackoverflow.com/questions/61395486/read-csv-file-and-return-indented-menu-c-sharp by sommmen
using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public class Node<T>
{
public T Data {get;set;}
public List<Node<T>> Children { get; set;}
public Node()
{
Children = new List<Node<T>>();
}
// Tree traversal in level order
public List<Node<T>> LevelOrder()
{
List<Node<T>> list = new List<Node<T>>();
Queue<Node<T>> queue = new Queue<Node<T>>();
queue.Enqueue(this);
while(queue.Count != 0)
{
Node<T> temp = queue.Dequeue();
foreach (Node<T> child in temp.Children)
queue.Enqueue(child);
list.Add(temp);
}
return list;
}
public List<Node<T>> PreOrder()
{
List<Node<T>> list = new List<Node<T>>();
list.Add(this);
foreach (Node<T> child in Children)
list.AddRange(child.PreOrder());
return list;
}
public List<Node<T>> PostOrder()
{
List<Node<T>> list = new List<Node<T>>();
foreach (Node<T> child in Children)
list.AddRange(child.PreOrder());
list.Add(this);
return list;
}
}
public class Entity
{
public int id {get;set;}
public string menuName {get;set;}
public int? parentID {get;set;}
public bool isHidden {get;set;}
public string linkURL {get;set;}
}
public static void Main()
{
var data = #"ID;MenuName;ParentID;isHidden;LinkURL
1;Company;NULL;False;/company
2;About Us;1;False;/company/aboutus
3;Mission;1;False;/company/mission
4;Team;2;False;/company/aboutus/team
5;Client 2;10;False;/references/client2
6;Client 1;10;False;/references/client1
7;Client 4;10;True;/references/client4
8;Client 5;10;True;/references/client5
10;References;NULL;False;/references";
var lines = data.Split('\n');
var rootNodes = new List<Node<Entity>>();
var childItems = new List<Entity>();
// Parse the data to entities
// Items without a parent are used as rootnodes to build a tree
foreach(var row in lines.Skip(1))
{
var columns = row.Split(';');
var id = Convert.ToInt32(columns[0]);
var menuName = columns[1];
var parentID = ToNullableInt(columns[2]);
var isHidden = Convert.ToBoolean(columns[3]);
var linkURL = columns[4];
var entity = new Entity()
{
id = id,
menuName = menuName,
parentID = parentID,
isHidden = isHidden,
linkURL = linkURL
};
if(parentID == null)
{
Console.WriteLine("Found root:" + entity.id + " - " + entity.menuName);
rootNodes.Add(new Node<Entity>()
{
Data = entity
});
}
else
{
childItems.Add(entity);
}
}
// Add the childElements to their appropriate rootnode
foreach(var rootNode in rootNodes)
{
foreach(var childItem in childItems.OrderBy(a=>a.parentID).ThenBy(b=>b.menuName))
{
var newNode = new Node<Entity>()
{
Data = childItem
};
Insert(rootNode, newNode);
}
}
Console.WriteLine("-------------------");
foreach(var rootNode in rootNodes)
{
var indent = 0;
var previous = rootNode;
foreach(var node in rootNode.LevelOrder())
{
if(node.Data.isHidden) continue;
if(previous.Data.parentID != node.Data.parentID)
indent++;
for(var i = 0; i < indent; i++)
Console.Write("\t");
Console.WriteLine(node.Data.id + " - " + node.Data.menuName);
previous = node;
}
}
}
public static void Insert(Node<Entity> rootNode, Node<Entity> targetNode)
{
foreach(var current in rootNode.LevelOrder())
{
if(current.Data.id == targetNode.Data.parentID)
{
current.Children.Add(targetNode);
return;
}
}
}
public static int? ToNullableInt(string s)
{
int i;
if (int.TryParse(s, out i)) return i;
return null;
}
}
I'm trying to come up with an efficient solution to be able to query the entity list and order it correctly. I have created a singly linked list type structure in SQL DB schema. I am using GUID as my IDs but for simplicity, I'll use int here. I could solve this problem easily by having a SortOrder column on the DB but because of other requirements, this is how I have to implement this table.
I have a table structure that looks like the following entity model:
public class Record
{
public int ID;
public string Name;
public int? ChildID; //References the next record
}
My initial thought is to create a partial class like the following:
public partial class Record
{
public int SortOrder
{
get
{
//query table and loop through the entire list building it from
//the bottom and keeping count of the integer sort order and return
//the one specific to this record
}
}
}
However, this seems very inefficient to have to query the entire list every time and iterate through to find the SortOrder. Is there anything else I can leverage like a custom OrderBy function or anything? I'm trying sort by the order that would be created when iterating a building the list. For instance, the record with ChildID = null, is the last one in the list, since it does not have a child. I'll start with that record, then get the next one above it that references the previous as its ChildID and go until there is no more in the list that has a reference to ID, which should be when the list is complete and ordered correctly. No two records have the same ChildID.
If I had the following 3 records in a list,
ID = 3, Name = "Apple", ChildID = 6,
ID = 54, Name = "Orange", ChildID = 3,
ID = 6, Name = "Banana", ChildID = null
Then I would expect to get Orange, Apple, Banana, in that order.
One way to do it would be to write a method that will return a list in sorted order. You would first find the record with ChildId == null, add it to the results list, and then continue to search for items where item.ChildId == previousItem.Id, and then insert them at the beginning of the list:
private static IEnumerable<Record> OrderRecords(IReadOnlyCollection<Record> records)
{
// "Exit fast" checks
if (records == null) return null;
if (records.Count < 2) return records.ToList();
// Get last record and add it to our results
Record currentRecord = records.Single(r => r.ChildID == null);
var results = new List<Record> {currentRecord};
// Keep getting the parent reference to the previous record
// and insert it at the beginning of the results list
while ((currentRecord = records.SingleOrDefault(r =>
r.ChildID == currentRecord.ID)) != null)
{
results.Insert(0, currentRecord);
}
return results;
}
In use, this would look something like:
private static void Main()
{
var records = new List<Record>
{
new Record {ID = 3, Name = "Apple", ChildID = 6},
new Record {ID = 54, Name = "Orange", ChildID = 3},
new Record {ID = 6, Name = "Banana", ChildID = null}
};
var sortedRecords = OrderRecords(records);
Console.WriteLine(string.Join(", ", sortedRecords.Select(r => r.Name)));
Console.Write("\nPress any key to exit...");
Console.ReadKey();
}
Output
Given that the record ID order is random, and assuming that the List you are ordering is complete, or that you won't run out of memory/time if you have to scan the entire table to order the list, I think the best you can do is compute the depth for a Record and cache the results:
I am using the List as the table, but you could use the table instead if the list you want to order is incomplete:
public partial class Record {
static Dictionary<int, int> depth = new Dictionary<int, int>();
public int Depth(List<Record> dbTable) {
int ans = 0;
var working = new Queue<int>();
var cur = this;
do {
if (depth.TryGetValue(cur.ID, out var curDepth)) {
ans += curDepth;
break;
}
else {
working.Enqueue(cur.ID);
cur = dbTable.FirstOrDefault(r => r.ChildID == cur.ID);
if (cur != null)
++ans;
}
} while (cur != null);
var workAns = ans;
while (working.Count > 0) {
var workingID = working.Dequeue();
depth.Add(workingID, workAns);
--workAns;
}
return ans;
}
}
Update: I re-wrote the code to use a specific queue; my first version was recursive and that was straightforward but risked overflowing the stack and my second version didn't cache the intermediate results when following the linked list which wasn't very efficient. Using a queue of the intermediate IDs ensures I only follow a particular chain depth once.
Now that you have a Depth method, sorting is easy:
var ans = work.OrderBy(w => w.Depth(work));
The best algorithm for this task is to prepare a fast lookup data structure (like Dictionary) of Record by ChildID. Then the ordered result can be produced backwards starting with ChildID = null and using the record ID to find the previous record.
Since the hash lookup time complexity is O(1), the time complexity of the algorithm is linear O(N) - the fastest possible.
Here is the implementation:
static Record[] Ordered(IEnumerable<Record> records)
{
var recordByNextId = records.ToDictionary(e => e.ChildID.Wrap());
var result = new Record[recordByNextId.Count];
int? nextId = null;
for (int i = result.Length - 1; i >=0; i--)
nextId = (result[i] = recordByNextId[nextId]).ID;
return result;
}
The explanation of e.ChildID.Wrap() custom extension method. I wish I can use simply e.ChildID, but the BCL Dictionary class throws annoying exception for null key. To overcome that limitation in general, I use a simple wrapper struct and "fluent" helper:
public struct ValueWrapper<T> : IEquatable<ValueWrapper<T>>
{
public readonly T Value;
public ValueWrapper(T value) => Value = value;
public bool Equals(ValueWrapper<T> other) => EqualityComparer<T>.Default.Equals(Value, other.Value);
public override bool Equals(object obj) => obj is ValueWrapper<T> other && Equals(other);
public override int GetHashCode() => EqualityComparer<T>.Default.GetHashCode(Value);
public static implicit operator ValueWrapper<T>(T x) => new ValueWrapper<T>(x);
public static implicit operator T(ValueWrapper<T> x) => x.Value;
}
public static class ValueWrapper
{
public static ValueWrapper<T> Wrap<T>(this T value) => new ValueWrapper<T>(value);
}
Question moved here.
My requirement is to write a program that sort of mimics diff tools. Yes there are quite a few libraries and open source code that accomplishes this purpose, but I would like to write my own comparer.
Here's the starting point. I have a class called DataItem which looks like this:
public class DataItem
{
public DataItem() { }
public DataItem(string d, string v) { Data = d; Value = v; }
public string Data { get; set; }
public string Value { get; set; }
}
I have two lists of these class objects, let's call them PRE and POST and take some example values as follows. 'Data' part will be unique in a list.
preList: (Data,Value)
AAA,0
BBB,1
CCC,3
DDD,4
FFF,0
GGG,3
postList: (Data,Value)
AAA,0
BBB,2
DDD,4
EEE,9
FFF,3
Think of PRE as the original list, and POST as the list after some changes done. I would like to compare the two, and categorize them into three categories:
Added Items - An item with a new 'Data' added to the list.
Removed Items - An item was removed from the list.
Diff Items - 'Data' is found in both PRE and POST lists, but their corresponding 'Value' is different.
So when categorized they should look like this:
Added Items:
EEE,9
Removed Items:
CCC,3
GGG,3
Diff Items:
BBB
FFF
I have another DiffItem class, to objects of which I would like to put the final results. DiffItem looks like this:
public class DiffItem
{
public DiffItem() { }
public DiffItem(string data, string type, string pre, string post) { Data = data; DiffType = type; PreVal = pre; PostVal = post; }
public string Data { get; set; }
public string DiffType { get; set; } // DiffType = Add/Remove/Diff
public string PreVal { get; set; } // preList value corresponding to Data item
public string PostVal { get; set; } // postList value corresponding to Data item
}
To accomplish this, first I extended IEqualityComparer and wrote a couple of comparers:
public class DataItemComparer : IEqualityComparer<DataItem>
{
public bool Equals(DataItem x, DataItem y)
{
return (string.Equals(x.Data, y.Data) && string.Equals(x.Value, y.Value));
}
public int GetHashCode(DataItem obj)
{
return obj.Data.GetHashCode();
}
}
public class DataItemDataComparer : IEqualityComparer<DataItem>
{
public bool Equals(DataItem x, DataItem y)
{
return string.Equals(x.Data, y.Data);
}
public int GetHashCode(DataItem obj)
{
return obj.Data.GetHashCode();
}
}
Then used Except() and Intersect() methods as follows:
static void DoDiff()
{
diffList = new List<DiffItem>();
IEnumerable<DataItem> preOnly = preList.Except(postList, new DataItemComparer());
IEnumerable<DataItem> postOnly = postList.Except(preList, new DataItemComparer());
IEnumerable<DataItem> common = postList.Intersect(preList, new DataItemComparer());
IEnumerable<DataItem> added = postOnly.Except(preOnly, new DataItemDataComparer());
IEnumerable<DataItem> removed = preOnly.Except(postOnly, new DataItemDataComparer());
IEnumerable<DataItem> diffPre = preOnly.Intersect(postOnly, new DataItemDataComparer());
IEnumerable<DataItem> diffPost = postOnly.Intersect(preOnly, new DataItemDataComparer());
foreach (DataItem add in added)
{
diffList.Add(new DiffItem(add.Data, "Add", null, add.Value));
}
foreach (DataItem rem in removed)
{
diffList.Add(new DiffItem(rem.Data, "Remove", rem.Value, null));
}
foreach (DataItem pre in diffPre)
{
DataItem post = diffPost.First(x => x.Data == pre.Data);
diffList.Add(new DiffItem(pre.Data, "Diff", pre.Value, post.Value));
}
}
This does work and gets the job done. But I'm wondering if there's a 'better' way to do this. Note that I put quotes around the word 'better', because I don't have a proper definition for what would make this better. Perhaps is there a way to get this done without as many 'foreach' loops and use of Except() and Intersetc(), since I would imagine that behind the Linq there's quite a bit of iterations going on.
Simply put, is there a cleaner code that I can write for this? I'm asking mostly out of academic interest and to expand my knowledge.
I don't think you need your IEqualityComparer:
var added = from a in postList
where !preList.Any(b => b.Data == a.Data)
select new DiffItem(a.Data, "Add", null, a.Value);
var removed = from b in preList
where !postList.Any(a => a.Data == b.Data)
select new DiffItem(b.Data, "Remove", b.Value, null);
var diff = from b in preList
join a in postList on b.Data equals a.Data
where b.Value != a.Value
select new DiffItem(b.Data, "Diff", b.Data, a.Data);
var diffList = added.ToList();
diffList.AddRange(removed);
diffList.AddRange(diff);
I am trying to figure out the best way to organise a bunch of my data classes, given I need to be able to access some metrics on them all at some point.
Here's a snippet of my OR class:
public enum status { CLOSED, OPEN }
public class OR
{
public string reference { get; set; }
public string title { get; set; }
public status status { get; set; }
}
Not every OR I initialise will have values for all properties. I want to be able to 'collect' thousands of these together in such a way that I can easily obtain a count of how many OR objects had a value set. For example:
OR a = new OR() { reference = "a" }
OR b = new OR() { reference = "b", title = "test" }
OR c = new OR() { reference = "c", title = "test", status = status.CLOSED }
Now these are somehow collected in such a way I can do (pseudo):
int titleCount = ORCollection.titleCount;
titleCount = 2
I would also want to be able gather metrics for the enum type properties, for example retrieve a Dictionary from the collection that looks like:
Dictionary<string, int> statusCounts = { "CLOSED", 1 }
The reason for wanting access to these metrics is that I am building two collections of ORs and comparing them side-by-side for any differences (they should be identical). I want to be able to compare their metrics at this higher level first, then break-down where precisely they differ.
Thanks for any light that can be shed on how to accomplish this. :-)
... to 'collect' thousands of these
Thousands is not a huge number. Just use a List<OR> and you can get all your metrics with Linq queries.
For example:
List<OR> orList = ...;
int titleCount = orList
.Where(o => ! string.IsNullOrEmpty(o.title))
.Count();
Dictionary<status, int> statusCounts = orList
.GroupBy(o => o.status)
.ToDictionary(g => g.Key, g => g.Count());
The existing answers using Linq are absolutely great and really elegant, so the idea presented below is just for posterity.
Here is a (very rough) reflection-based program that will alow you to count the "valid" properties in any collection of objects.
The validators are defined by you in the Validators dictionary so that you can easily change what is a valid/invalid value for each property. You may find it useful as a concept if you end up with objects having tons of properties and don't want to have to write inline linq metrics on the actual collection itself for every single property.
You could weaponise this as a function and then run it against both collections, giving you a basis to report on the exact differences between both since it records the references to the individual objects in the final dictionary.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace reftest1
{
public enum status { CLOSED, OPEN }
public class OR
{
public string reference { get; set; }
public string title { get; set; }
public status status { get; set; }
public int foo { get; set; }
}
//creates a dictionary by property of objects whereby that property is a valid value
class Program
{
//create dictionary containing what constitues an invalid value here
static Dictionary<string,Func<object,bool>> Validators = new Dictionary<string, Func<object,bool>>
{
{"reference",
(r)=> { if (r ==null) return false;
return !String.IsNullOrEmpty(r.ToString());}
},
{"title",
(t)=> { if (t ==null) return false;
return !String.IsNullOrEmpty(t.ToString());}
},
{"status", (s) =>
{
if (s == null) return false;
return !String.IsNullOrEmpty(s.ToString());
}},
{"foo",
(f) =>{if (f == null) return false;
return !(Convert.ToInt32(f.ToString()) == 0);}
}
};
static void Main(string[] args)
{
var collection = new List<OR>();
collection.Add(new OR() {reference = "a",foo=1,});
collection.Add(new OR(){reference = "b", title = "test"});
collection.Add(new OR(){reference = "c", title = "test", status = status.CLOSED});
Type T = typeof (OR);
var PropertyMetrics = new Dictionary<string, List<OR>>();
foreach (var pi in GetProperties(T))
{
PropertyMetrics.Add(pi.Name,new List<OR>());
foreach (var item in collection)
{
//execute validator if defined
if (Validators.ContainsKey(pi.Name))
{
//get actual property value and compare to valid value
var value = pi.GetValue(item, null);
//if the value is valid, record the object into the dictionary
if (Validators[pi.Name](value))
{
var lookup = PropertyMetrics[pi.Name];
lookup.Add(item);
}
}//end trygetvalue
}
}//end foreach pi
foreach (var metric in PropertyMetrics)
{
Console.WriteLine("Property '{0}' is set in {1} objects in collection",metric.Key,metric.Value.Count);
}
Console.ReadLine();
}
private static List<PropertyInfo> GetProperties(Type T)
{
return T.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
}
}
}
You can get the title count using this linq query:
int titleCount = ORCollection
.Where(x => !string.IsNullOrWhiteSpace(x.title))
.Count();
You could get the count of closed like this:
int closedCount = ORCollection
.Where(x => x.status == status.CLOSED)
.Count();
If you were going to have larger collections or you access the values a lot it might be worth creating a custom collection implementation that stores the field counts, it could then increment/decrement these values as you add and remove items. You could also store a dictionary of status counts in this custom collection that gets updated as you add and remove items.
Let's say we have a Windows app that has a TreeView and you can expand the nodes of this view and drill down to the children node, they may also have more children so now we can expand that node and go further, etc.. So in my source code I have a method Foo(string fatherNode) that gets the father node that we clicked on and finds the children and lists them:
the high level body of this method is like this:
private void Foo(string fatherNode)
{
// call some DB scripts and grab data you need to work with.
int numberOfKids = // get it from the thing you populated from the DB call.
for(int i = 1 to numberOfKids)
{
Node Child = // grab child[i] from the list we populated from DB calls
//Add it to the treeView
}
}
Well that code is good for a UI app, where we click on a node, it runs this method once and collects the data it needs, Now I need to write another method utilizing useful lines of the method above Grab EVERYTHING at once and write the Whole information to let's say a file.
So in my head in looks like a recursive method to me. But still can't figure out the whole picture, Prob should have two collections, one for fathers, one for kids, loop through kids and make recursive call to get more kids and add them to the collection, etc
I was wondering if you can clear out the fogs, the high-level of what I need to do to, how the colections should look like, where to add to them, where to call the recursive method call, etc...and please don't specifically think about a "treeview" object, I just used that as an example to expalin the question better. The main thing is the structure of the Foo method that I posted. That's what I should work with.
Well, I'm not sure if this is what you're looking for, even after the other answers.
However, please, check it out:
The self-related entity (Node)
public class MyEntity
{
public MyEntity() { }
public MyEntity(string Name, int ID, int? ParentID)
{
this.Name = Name;
this.ID = ID;
this.ParentID = ParentID;
}
public string Name { get; set; }
public int ID { get; set; }
public int? ParentID { get; set; }
}
The tree building methods
public static StringBuilder GetFamilyTree(List<MyEntity> AllTheEntities)
{
StringBuilder Return = new StringBuilder();
List<MyEntity> OrderedEntities = AllTheEntities.OrderBy<MyEntity, int>(x => x.ID).ToList();
foreach (MyEntity CurrentEntity in AllTheEntities.Where<MyEntity>(x => !x.ParentID.HasValue))
{
Return.AppendLine(GetEntityTree(AllTheEntities, CurrentEntity));
}
return Return;
}
public static string GetEntityTree(List<MyEntity> AllTheEntities, MyEntity CurrentEntity, int CurrentLevel = 0)
{
StringBuilder Return = new StringBuilder();
Return.AppendFormat("{0}{1}", "\t".Repeat(CurrentLevel), CurrentEntity.Name);
Return.AppendLine();
List<MyEntity> Children = AllTheEntities.Where<MyEntity>(x => x.ParentID.HasValue && x.ParentID.Value == CurrentEntity.ID).ToList();
if (Children != null && Children.Count > 0)
{
foreach (MyEntity CurrentChildEntity in Children)
{
Return.Append(GetEntityTree(AllTheEntities, CurrentChildEntity, CurrentLevel + 1));
}
}
return Return.ToString();
}
A small helper class
public static class StringExtension
{
public static string Repeat(this string text, int times)
{
string Return = string.Empty;
if (times > 0)
{
for (int i = 0; i < times; i++)
{
Return = string.Concat(Return, text);
}
}
return Return;
}
}
Usage
List<MyEntity> AllMyEntities = new List<MyEntity>();
AllMyEntities.Add(new MyEntity("1", 1, null));
AllMyEntities.Add(new MyEntity("1.1", 2, 1));
AllMyEntities.Add(new MyEntity("1.1.1", 3, 2));
AllMyEntities.Add(new MyEntity("2", 4, null));
AllMyEntities.Add(new MyEntity("2.1", 5, 4));
Console.Write(GetFamilyTree(AllMyEntities).ToString());
Results
1
1.1
1.1.1
2
2.1
Call Foo(child) within for loop. I guess that will solve your problem. If tree is huge don't recurse. Use Stack.
You want to make a simple tree traversal algorithm. Here is a simple implementation of a DFS (Depth first search) in pseudo code:
TraverseTree(Tree t)
{
DoSomethingWith(t); // like writing the contents of the node to the file.
if (t == null) // leaf
return;
foreach(Tree child in t.Children) // recursively traverse the children.
{
TraverseTree(child);
}
}
You can play with the order you execute your computation. see more details here