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
Related
I recently started making a Binary Search Tree in C# in order to practice. After completing this task I decided to implement ICollection<T> so that my tree can be used with a foreachand just in general in order for me to practice.
I've constructed my classes in such a way that I have a Node<T> class and a BinarySearchTree<T> class that contains a Node<T> a Count integer and a IsReadOnly boolean. This is my Node Class:
internal class Node<T>: INode<T> where T: IComparable<T>
{
public Node<T> RightChildNode { get; set; }
public Node<T> LeftChildNode { get; set; }
public T Key { get; set; }
//some methods go here
}
and this is my BST class:
public class BinarySearchTree<T>: ICollection<T> where T: IComparable<T>
{
internal Node<T> Root { get; set; }
public int Count { get; private set; }
public bool IsReadOnly => false;
//some methods go here
}
Now in order to implement ICollection<T> i obviously need an enumerator which I have (partly) implemented as such:
internal class BinarySearchTreeEnumerator<T> : IEnumerator<T> where T: IComparable<T>
{
private BinarySearchTree<T> _parentTree;
private BinarySearchTree<T> _currentTree;
private Node<T> _currentNode => _currentTree.Root;
private T _currentKey;
public T Current => _currentNode.Key;
/// <summary>
/// generic constructor
/// </summary>
/// <param name="tree"></param>
public BinarySearchTreeEnumerator(BinarySearchTree<T> tree)
{
this._parentTree = tree;
}
object IEnumerator.Current => Current;
void IDisposable.Dispose(){}
//pls
public bool MoveNext()
{
if (_currentTree is null)
{
_currentTree = _parentTree;
}
var leftSubtree = this._currentTree.GetLeftSubtree();
var rightSubtree = this._currentTree.GetRightSubtree();
if (!(leftSubtree is null))
{
this._currentTree = leftSubtree;
}
else if (!(rightSubtree is null))
{
this._currentTree = rightSubtree;
}
else
{
}
}
public void Reset()
{
_currentTree = _parentTree;
}
}
now my issue is quite obviously with the MoveNext() method. It doesn't work now because what it does is it just goes down the tree on the leftmost possible path and then gets stuck when it gets to the end of that path. I know I can fix this problem by adding a Parent property to my Node<T> class and then whenever I reach the end of a path in my tree I can just go one Node up and check if there's a different path... However this would mean completely changing my original class and I would prefer not to do that.
Is this just unavoidable? Is there any way to solve this issue without changing my Node<T> class in such a way?
Edit: I Made a thing but its not working :/
public bool MoveNext()
{
if (_currentNode is null)
{
this._currentNode = _parentTree.Root;
this._nodeStack.Push(_currentNode);
return true;
}
var leftNode = this._currentNode.LeftChildNode;
var rightNode = this._currentNode.RightChildNode;
if (!(leftNode is null))
{
this._currentNode = leftNode;
this._nodeStack.Push(_currentNode);
return true;
}
else if (!(rightNode is null))
{
this._currentNode = rightNode;
this._nodeStack.Push(_currentNode);
return true;
}
else
{
//current node does not have children
var parent = this._nodeStack.Pop();
do
{
if (parent is null)
{
return false;
}
} while (!(parent.RightChildNode is null));
this._currentNode = parent.RightChildNode;
this._nodeStack.Push(_currentNode);
return true;
}
}
It might be easier to use recursion to implement this; for example:
Recursive version (for balanced trees only)
public IEnumerator<T> GetEnumerator()
{
return enumerate(Root).GetEnumerator();
}
IEnumerable<T> enumerate(Node<T> root)
{
if (root == null)
yield break;
yield return root.Key;
foreach (var value in enumerate(root.LeftChildNode))
yield return value;
foreach (var value in enumerate(root.RightChildNode))
yield return value;
}
These are members of BinarySearchTree<T>.
Given the above implementation, then the following code:
BinarySearchTree<double> tree = new BinarySearchTree<double>();
tree.Root = new Node<double> {Key = 1.1};
tree.Root.LeftChildNode = new Node<double> {Key = 2.1};
tree.Root.RightChildNode = new Node<double> {Key = 2.2};
tree.Root.LeftChildNode.LeftChildNode = new Node<double> { Key = 3.1 };
tree.Root.LeftChildNode.RightChildNode = new Node<double> { Key = 3.2 };
tree.Root.RightChildNode.LeftChildNode = new Node<double> { Key = 3.3 };
tree.Root.RightChildNode.RightChildNode = new Node<double> { Key = 3.4 };
foreach (var value in tree)
{
Console.WriteLine(value);
}
produces this output:
1.1
2.1
3.1
3.2
2.2
3.3
3.4
WARNING: Stack space is limited to 1MB for a 32-bit process and 4MB for a 64-bit process, so using recursion is likely to run out of stack space if the tree is degenerate (badly unbalanced).
Non-recursive version
You can implement the non-recursive version fairly simply, like so:
IEnumerable<T> enumerate(Node<T> root)
{
var stack = new Stack<Node<T>>();
stack.Push(root);
while (stack.Count > 0)
{
var node = stack.Pop();
if (node == null)
continue;
yield return node.Key;
stack.Push(node.RightChildNode);
stack.Push(node.LeftChildNode);
}
}
This returns the elements in the same order as the recursive version.
Since this version will not run out of stack space even for a degenerate tree, it is preferable to the recursive version.
If you add to your enumerator a
private List<Node<T>> _currentParentNodes = new List<Node<T>>();
and use it like a stack, each time you go down a level you push the current node to currentParentNodes, each time you have to go up you pop the parent node from currentParentNodes, then all your problems will pop away :-)
Do you need a depth-first-search(DFS) approach? It has a recursive nature which is hard to save as a state (it uses the call stack).
Maybe consider the broad-first-search (BFS) approach, which is iterative. You'd only need a currentNode and a Queue.
The rule would be
current = Queue.poll();
For child in current
Queue.offer(child);
At init you would do
Queue.offer(rootNode);
I apologize for my poor formatting and syntax, mobile user here.
Andres
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);
}
How can I modify the nested elements inside of an IEnuemerable<ProductLineDto>
So basically the loops look like this product lines > project types > projects > sub projects > which consists of more sub projects and activities.
The problem is I can't modify the activities of a subproject which is supposed to be happening in the recursive function GetActivitiesForActivityPerson
private IEnumerable<ProductLineDto> GetGanttDataByPerson(IEnumerable<ProductLineDto> ganttData, GanttFilterDto ganttFilterDataModel)
{
if(ganttFilterDataModel.PersonId == null)
return ganttData;
var gdata = ganttData.CopyProductLineDtosEnumerable().ToList();
for(var i = 0; i < gdata.Count; i++)
{
var pType = gdata[i].ProjectType.CopyProjectTypeDtoEnumerable().ToList();
for (var j = 0; j < pType.Count; j++)
{
var projects = pType[j].Projects.CopyProjectDtosEnumerable().ToList();
for (var a = 0; a < projects.Count; a++)
{
// projects[a].Children is not being modified
projects[a].Children = GetActivitiesForActivityPerson(projects[a].Children, ganttFilterDataModel.PersonId);
}
pType[j].Projects = projects;
}
gdata[i].ProjectType = pType;
}
ganttData = gdata;
return ganttData; ;
}
here is the recursive function
private IEnumerable<SubProjectDto> GetActivitiesForActivityPerson(IEnumerable<SubProjectDto> children, Guid? personId)
{
var subProjects = children.CopySubProjectDtoEnumerable().ToList();
for (var i = 0; i < subProjects.Count; i++)
{
var acts = subProjects[i].Activities.CopyActivityDtoEnumerable().ToList();
acts.RemoveAll(activity => activity.ActivityPersons.All(person => person.PersonID != personId));
subProjects[i].Activities = acts;
if(subProjects[i].Children.Any())
GetActivitiesForActivityPerson(subProjects[i].Children, personId);
}
children = subProjects;
return children;
}
When I step through the recursive function, the return children statement consists of the modified activities. However, this line of code does not consist of the modified activities it consists of the original data set after the assignment call.
projects[a].Children = GetActivitiesForActivityPerson(projects[a].Children, ganttFilterDataModel.PersonId);
If anyone is wondering what all of the copy methods look like, They just return a new instance of the DTO.
for example
public static IEnumerable<ProjectTypeDto> CopyProjectTypeDtoEnumerable(this IEnumerable<ProjectTypeDto> projectTypes)
{
return projectTypes.Select(x => new ProjectTypeDto
{
ProjectTypeId = x.ProjectTypeId,
Description = x.Description,
RowId = x.RowId,
Projects = x.Projects.CopyProjectDtosEnumerable()
});
}
For future reference, please submit an executable excerpt of code. For best results, you want people to be able to copy-past your problem code straight into their IDEs to play with.
Also, the code was painfully convoluted. It simplifies to this:
using System;
using System.Collections.Generic;
using System.Linq;
namespace StackOverflow_LoopMadness
{
class Program
{
static void Main(string[] args)
{
// ???
}
private static IEnumerable<ProductLineDto> GetGanttDataByPerson(IEnumerable<ProductLineDto> ganttData, GanttFilterDto ganttFilterDataModel)
{
if (ganttFilterDataModel.PersonId == null)
return ganttData;
foreach (var pType in ganttData)
{
foreach (var project in pType.Projects)
{
project.Children = GetActivitiesForActivityPerson(project.Children, ganttFilterDataModel.PersonId);
}
}
return ganttData;
}
private static IEnumerable<SubProjectDto> GetActivitiesForActivityPerson(IEnumerable<SubProjectDto> children, Guid? personId)
{
foreach (var subProject in children)
{
subProject.Activities = subProject.Activities.Where(a => a.ActivityPersons.All(person => person.PersonID != personId));
if (subProject.Children.Any())
{
GetActivitiesForActivityPerson(subProject.Children, personId);
}
}
return children;
}
}
class SubProjectDto
{
public IEnumerable<Activity> Activities { get; internal set; }
public IEnumerable<SubProjectDto> Children { get; internal set; }
}
class Activity
{
public IList<ActivityPerson> ActivityPersons { get; internal set; }
}
class ActivityPerson
{
public Guid? PersonID { get; internal set; }
}
class GanttFilterDto
{
public Guid PersonId { get; internal set; }
}
class ProductLineDto
{
public IList<Project> Projects { get; internal set; }
}
class Project
{
public IEnumerable<SubProjectDto> Children { get; set; }
}
}
As you can see, swapping the for-loops with foreach-loops greatly decreases the amount of code and increases readability. The extraneous copy lists were removed. Moreover, it was necessary for me to create 6 data classes via guesswork in order to compile.
I gave up figuring out a suitable driver for this program. However, I'm confident the problem of modifying nested elements is solved, because the edited variables are only passed by reference.
I think your problem stems from an incomplete understanding of pass by value vs. reference, thus your confusing use of deep copies. Microsoft can explain it better than me, so please check out this link.
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).
My domain object :
public class MyDomainObject
{
public Guid Id { get; set; }
public string Name { get; set; }
public int DisplayOrder { get; set; }
}
Assuming sample data :
var list = new List<MyDomainObject>()
{
new MyDomainObject {Name = "Element1", DisplayOrder = 0},
new MyDomainObject {Name = "Element2", DisplayOrder = 1},
new MyDomainObject {Name = "Element3", DisplayOrder = 2},
new MyDomainObject {Name = "Element4", DisplayOrder = 3},
};
Now i change the DisplayOrder of the "Element3" from 2 to 1. My list should looks like that :
Element1 (DisplayOrder = 0)
Element3 (DisplayOrder = 1)
Element2 (DisplayOrder = 2)
Element4 (DisplayOrder = 3)
Now i remove "Element3"
Element1 (DisplayOrder = 0)
Element2 (DisplayOrder = 1)
Element4 (DisplayOrder = 2)
So what's the best way to persist this mechanism to database ?
Basically i need a "ReOrderableCollection" which will be populated from database with an OrderBy "DisplayOrder" where Collection Index Match "DisplayOrder", and persist back items by assigning DisplayOrder from Collection Index.
I answered a previous/similar question about re-ordering here:
How to design table that can be re-sequenced?
This does a good job of resaving the Order with no gaps. Depending on the size the lists resaving the Order may be a perfectly viable option, for long lists Mark Byers' idea looks pretty good.
From your examples it seems that you always want the sequence to be without gaps, starting from zero. But this means that removing the first element will require updating the row in the database for every single item in your list. It's simple and it will work (and these are good things) but it's not always ideal. Since you asked for "the best way" without really specifying what you mean by that, allow me to suggest an alternative method:
What really matters with a DisplayOrder is not the actual values but their relative order. If you want to improve performance with the database, you could consider relaxing the requirement that there should be no gaps and then try to find the smallest number of changes to the DisplayOrders to ensure that the correct order is stored, even if gaps are present in the resulting sequence. If you do this then adding, removing or moving a single item will typically only require updating one row in the database, with the exception that occasionally other items will have to be moved to create a gap where an item must be inserted between two others that have consecutive DisplayOrders.
You can also minimize the number of times that a gap is not available by starting with DisplayOrder 100, 200, 300 and later allowing for example an insertion with DisplayOrder 150 in between (or perhaps use a real/float type instead of an integer).
Another advantage of this method is if you use a database data comparison tool to observe changes between the current version of the database and older versions it will be easier to see what modifications have been made to the display order. You will only see changes in the display order of items that have actually been moved by the user, rather than half the list change each time an item is removed. It will also reduce the size of backups if you use an incremental backup strategy.
I'd say though that these advantages are not significant advantages over the naive method for most cases. It depends on your system whether it is worth implementing this system or just keeping it simple. If in doubt, keep it simple. For systems with small lists, few modifications and where you don't care about the change history, overwriting the entire list with new DisplayOrders each time will probably be just fine.
For what I can see it seems that DisplayOrder has the same value of the index property of the collection. So I will try to use that instead of a DisplayOrder property. On the DB I will use the DisplayOrder column to read and save the items but not on the domain objects.
HTH
ema
Now I'm assuming that you do want to always reorganize your list so that the DisplayOrder starts at 0 and increases without gaps, and you want this to happen automatically. You could implement your own collection type and an interface IDisplayOrderable and have the members of your type that change the list also automaticaly update the DisplayOrder of the items in the collection. As opposed to my other answer which was about an alternative way to store the data in the datase, this answer shows how to write a client class that could make it easier to automatically synchronize the DisplayOrder in your objects with your list indexes so that when you are ready to submit the changes to the database, the DisplayOrder field is already set correctly for you.
I think the answer is best given as some source code:
using System;
using System.Collections.Generic;
using System.Linq;
interface IDisplayOrderable
{
int DisplayOrder { get; set; }
}
class ReorderableList<T> : IList<T> where T : IDisplayOrderable
{
List<T> list = new List<T>();
private void updateDisplayOrders()
{
int displayOrder = 0;
foreach (T t in list)
{
t.DisplayOrder = displayOrder++;
}
}
public ReorderableList() { }
public ReorderableList(IEnumerable<T> items)
{
list = new List<T>(items.OrderBy(item => item.DisplayOrder));
}
public void Insert(int index, T item)
{
list.Insert(index, item);
updateDisplayOrders();
}
public void Add(T item)
{
list.Add(item);
updateDisplayOrders();
}
public bool Remove(T item)
{
bool result = list.Remove(item);
if (result)
updateDisplayOrders();
return result;
}
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
// TODO: Other members and methods required to implement IList<T>...
}
class Item : IDisplayOrderable
{
public string Name { get; set; }
public int DisplayOrder { get; set; }
}
class Program
{
static void Main()
{
Item foo = new Item { Name = "foo", DisplayOrder = 0 };
Item bar = new Item { Name = "bar", DisplayOrder = 1 };
Item baz = new Item { Name = "baz", DisplayOrder = 2 };
// Pretend this came from the database.
IEnumerable<Item> query = new Item[] { bar, foo };
// The constructor automatically reorder the elements.
ReorderableList<Item> items = new ReorderableList<Item>(query);
items.Add(baz);
items.Remove(foo);
items.Insert(1, foo);
foreach (Item item in items)
Console.WriteLine("{0} : {1}", item.Name, item.DisplayOrder);
}
}
Output:
bar : 0
foo : 1
baz : 2
Perhaps this was the sort of answer you were looking for?
I maybe founded a solution by creating a custom List which take an Lamba Expression in constructor parameter in order the list to be able to self update items property "DisplayOrder".
The sample class
public class MyItem
{
public string Name { get; set; }
public int DisplayOrder { get; set; }
}
The sample program
public class Program
{
static void Main(string[] args)
{
var list = new DisplayOrderableList<MyItem>(p => p.DisplayOrder)
{
new MyItem{ Name = "Item 1"},
new MyItem{ Name = "Item 2"},
new MyItem{ Name = "Item 3"},
};
var item = list.Where(p => p.Name == "Item 2").FirstOrDefault();
list.MoveUp(item);
list.ForEach(p => Console.WriteLine("{0}-{1}", p.Name, p.DisplayOrder));
Console.WriteLine();
list.MoveDown(item);
list.ForEach(p => Console.WriteLine("{0}-{1}", p.Name, p.DisplayOrder));
Console.WriteLine();
Console.ReadLine();
}
}
The custom implementation of DisplayOrderableList
public class DisplayOrderableList<T> : List<T>
{
#region Private Fields
private PropertyInfo _property;
#endregion
#region Constructors
public DisplayOrderableList(Expression<Func<T, int>> expression)
{
ValidateExpression(expression);
}
#endregion
#region Public Methods
public void MoveUp(T item)
{
if (!Contains(item))
throw new ArgumentNullException("item", "item doesn't exists in collection");
var idx = IndexOf(item);
RemoveAt(idx);
if (idx > 0)
Insert(idx - 1, item);
else
Insert(0, item);
UpdateDisplayOrder();
}
public void MoveDown(T item)
{
if (!Contains(item))
throw new ArgumentNullException("item", "item doesn't exists in collection");
var idx = IndexOf(item);
RemoveAt(idx);
if (idx + 1 > Count)
Add(item);
else
Insert(idx + 1, item);
UpdateDisplayOrder();
}
#endregion
#region Private Methods
private void UpdateDisplayOrder()
{
foreach (var item in this)
{
_property.SetValue(item, IndexOf(item), null);
}
}
#endregion
#region Expression Methods
private void ValidateExpression(Expression<Func<T, int>> expression)
{
var lamba = ToLambaExpression(expression);
var propInfo = ToPropertyInfo(lamba);
if (!propInfo.CanWrite)
{
throw new ArgumentException(String.Format("Property {0} as no setters", propInfo.Name));
}
_property = propInfo;
}
private static LambdaExpression ToLambaExpression(Expression expression)
{
var lambda = expression as LambdaExpression;
if (lambda == null)
{
throw new ArgumentException("Invalid Expression");
}
var convert = lambda.Body as UnaryExpression;
if (convert != null && convert.NodeType == ExpressionType.Convert)
{
lambda = Expression.Lambda(convert.Operand, lambda.Parameters.ToArray());
}
return lambda;
}
private static PropertyInfo ToPropertyInfo(LambdaExpression expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression", "Expression cannot be null.");
}
var prop = expression.Body as MemberExpression;
if (prop == null)
{
throw new ArgumentException("Invalid expression");
}
var propInfo = prop.Member as PropertyInfo;
if (propInfo == null)
{
throw new ArgumentException("Invalid property");
}
return propInfo;
}
#endregion
}
This now get the following output :
Item 2-0
Item 1-1
Item 3-2
Item 1-0
Item 2-1
Item 3-2
It's a proof of concept and should be enhanced but it's a beggining.
What do you think about this ?
I know this is a old question, but the comments here and in another question helped me solve a similar issue and I wanted to provide my code in case it helps anyone else out looking for something similar. You can find my code at the following link:
How to design table that can be re-sequenced?