get tree Like Class children using recursion - c#

I built a class Cluster as follow:
public class Cluster
{
List<Cluster> lstChildClusters=new List<Cluster>();
public List<Cluster> LstChildClusters
{
get { return lstChildClusters; }
set { lstChildClusters = value; }
}
public classA alr;
}
My goal is to build a function that gets all the grandchildren of an object of Cluster type.Basically a father can have 0 or more sons which can have at their turn 0 or more sons.
I tried to build a recursive function but all it gives back is only one grandchild using the code down below.
Here is the function I built:
public List<classA> getLevel0Clusters(Cluster cluster,List<classA> list)
{
if (cluster.LstChildClusters.Count == 0)
{
list.Add(cluster.alr);
return (list);
}
else
{
for (int i = 0; i < lstChildClusters.Count - 1; i++)
{
return (lstChildClusters[i].getLevel0Clusters(lstChildClusters[i], list));
}
return (lstChildClusters[0].getLevel0Clusters(lstChildClusters[0], list));
}
}
I am using those instances for debugging:
Cluster father = new Cluster();
father.Alr = new Alarm("father");
Cluster son1 = new Cluster();
son1.Alr = new Alarm("son1");
Cluster son2 = new Cluster();
son2.Alr = new Alarm("son2");
Cluster grandson1 = new Cluster();
grandson1.Alr = new Alarm("grandson1");
Cluster grandson2 = new Cluster();
grandson2.Alr = new Alarm("grandson2");
father.LstChildClusters.Add(son1);
father.LstChildClusters.Add(son2);
son1.LstChildClusters.Add(grandson1);
son1.LstChildClusters.Add(grandson2);
List<classA> lst=new lst<ClassA>();
lst=father.getLevel0Clusters(father, father.LstAlarms);
Does anybody has any clue on how to troubleshoot this problem?
Thank you in advance

There are a number of problems with your existing code, so I've done a bit of refactoring to make your program simpler.
But first, to answer your direct question, the problem with your existing method is that you're calling return before you finish aggregating all of the results. Your code looks at grandfather and sees that it has children so it enters the for loop and recursively calls itself for son1. It sees that son1 has children so enters the for loop and recursively calls itself for grandson1 which doesn't have children so it adds grandson1 to the list and then returns. The outer call returns after finding the first value so the next two levels up just return. Hence the list only has grandson1.
So, to refactor your code: The getLevel0Clusters method does not need to pass in a Cluster (as it is defined in the Cluster class it can use this) and a List<classA> (as it can generate one as needed).
So your getLevel0Clusters can become simply this:
public List<classA> getLevel0Clusters()
{
return new[] { this.alr, }
.Concat(this.LstChildClusters
.SelectMany(child => child.getLevel0Clusters()))
.ToList();
}
In order to get everything to compile I modified your sample code to be this:
Cluster father = new Cluster();
father.alr = new classA("father");
Cluster son1 = new Cluster();
son1.alr = new classA("son1");
Cluster son2 = new Cluster();
son2.alr = new classA("son2");
Cluster grandson1 = new Cluster();
grandson1.alr = new classA("grandson1");
Cluster grandson2 = new Cluster();
grandson2.alr = new classA("grandson2");
father.LstChildClusters.Add(son1);
father.LstChildClusters.Add(son2);
son1.LstChildClusters.Add(grandson1);
son1.LstChildClusters.Add(grandson2);
List<classA> lst = father.getLevel0Clusters();
...and your classes as this:
public class Cluster
{
List<Cluster> lstChildClusters = new List<Cluster>();
public List<Cluster> LstChildClusters
{
get { return lstChildClusters; }
set { lstChildClusters = value; }
}
public classA alr;
public List<classA> getLevel0Clusters()
{
return new[] { this.alr, }
.Concat(this.LstChildClusters
.SelectMany(child => child.getLevel0Clusters()))
.ToList();
}
}
public class classA
{
public string Name;
public classA(string name)
{
this.Name = name;
}
}
When I ran your sample code I got this result out:

The problem is that as soon as you find one offspring, you return to the calling program. The value of Count has no effect other than 0 versus positive: you enter the loop, call lstChildClusters[0].getLevel0Clusters(lstChildClusters[0], and return that value without bothering to increment i and continue the loop.
Instead, your for loop has to add each return value to the list. After the loop is done, you can return to the calling program.

Related

How to get a certain part of a linked list

I have a project at my university and I stumbled upon a problem I am not able to solve.
About the program: I need to create a list of tasks(they can be private or business tasks). I need a function that returns a list of ONLY private tasks and another function that returns a list of ONLY business tasks.
So I have a class "Task" that contains "next" and "prev" connections. The classes "PrivateTask" and "BusinessTask" inherit this class. I also have a class ToDoList where I actually try to create the list.
class ToDoList
{
Task first = null;
Task last = null;
//adds new tasks and sorts them right away
public void AddSorted(Task newTask)
{
if(first == null)
{
first = newTask;
last = newTask;
}
else
{
if(newTask < first)
{
Prepend(newTask);
}
else if(newTask > last)
{
Append(newTask);
}
else
{
Task loopTask = first;
while(newTask > loopTask)
{
loopTask = loopTask.next;
}
AddBefore(loopTask, newTask);
}
}
}
//adds a new task before another chosen task
private void AddBefore(Task Next, Task newTask)
{
newTask.prev = Next.prev;
newTask.next = Next;
Next.prev.next = newTask;
Next.prev = newTask;
}
//adds at the start of the list
private void Prepend(Task newTask)
{
first.prev = newTask;
newTask.next = first;
first = newTask;
}
//adds at the end of the list
private void Append(Task newTask)
{
last.next = newTask;
newTask.prev = last;
last = newTask;
}
And now I need to return a list of BusinessTasks
//returns a list of business tasks
public ToDoList GetBusinessList()
{
ToDoList busList = new ToDoList();
Task loopTask = first;
while(loopTask != null)
{
if(loopTask is BusinessTask)
{
busList.AddSorted(loopTask);
}
loopTask = loopTask.next;
}
return busList;
}
But when I return this list the whole content of the main list synchronizes with this one and I cannot understand why.
You aren't putting copies of your tasks into your new list, you are putting references into the new list. As a result, you are changing the same objects. So when you push an item from your first list into the second list and as a result next and/or prev gets changed, you are changing both lists.
So you need to copy the item from your original list and put the new item in the second list.
while(loopTask != null)
{
if(loopTask is BusinessTask)
{
var clone = loopTask.Clone();
busList.AddSorted(clone);
}
loopTask = loopTask.next;
}
Now obviously you'll need to implement a Clone method that will copy all the properties except those that relate to the position in the list (prev and next) to a new instance of BusinessTask
Now if you actually want to have the object in both lists to be references to the same object. So that changing a property on one will change the other, then you can get clever by separating out the data part from the list node part. So you could do something like:
public class TaskBase
{
public string SomeProperty { get; set; }
}
public class Node
{
public TaskBase Data { get; private set;}
public Node Next { get; set; }
public Node Prev { get; set; }
public Node(TaskBase data)
{
Data = data;
}
public Node Clone()
{
// Now all the data part is the same object
// so changing Data.SomeProperty in one list will be
// reflected in both. But the Next and Prev properties
// are independent.
return new Node(Data);
}
}
And then your loop might look like this:
while(loopTask != null)
{
if(loopTask.Data is BusinessTask) // assuming BusinessTask derives from BaseTask
{
var clone = loopTask.Clone();
// clone contains the same BusinessTask, but it's position in the new list
// won't mess up the old list.
busList.AddSorted(clone);
}
loopTask = loopTask.next;
}

Sum up all the properties of a collection and dynamically assigned it to another object

I have a collection of object in lst of type DataResponse and what I would like to do is sum up all the properties that are int and decimal of this collection and assign the result of each property to another object DataContainerResponse that has the same exact property names(and types) as the those that are being summed up.
I can do this manually by typing out each property by hand and do a .Sum(s=>s.<propertyname>. But that so 90s. Below is my fruitless attempt to juice it out. Frankly, I never assigned a var to a lambda expression before and I don't even know if it's possible .Sum(s=><var name>);
public DataAggragationResponse doAggregation(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataContainerResponse rd = new DataContainerResponse();
//If I do it manually typing each prop by hand.
rd.VIOL = lst.Sum(s => s.VIOL);
//Automation!!!
foreach (PropertyInfo propertyInfo in typeof(DataResponse).GetProperties())
{
rd.GetType().GetProperties().SetValue(lst.Sum(s => propertyInfo.Name[0]));
}
}
If you want to go with full reflection, you can try something like the following. I didnt optimize the code, did it as fast as I can. So sorry for the messy look and Im assuming the property names are same in the aggregated result class and the unit class that you are aggregating against.
class Program
{
static void Main(string[] args)
{
var list = new List<DataResponse>();
list.Add(new DataResponse() { Stuff = 1, Stuff2 = 2 });
list.Add(new DataResponse() { Stuff = 1, Stuff2 = 2 });
Stopwatch watch = new Stopwatch();
watch.Start();
var response = DoAggregationReflection(list);
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
watch.Reset();
watch.Start();
var response2 = DoAggregation(list);
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
}
public static DataAggragationResponse DoAggregationReflection(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataAggragationResponse aggrResponse = new DataAggragationResponse();
var responseType = typeof(DataResponse);
var aggrResponseType = typeof(DataAggragationResponse);
foreach (PropertyInfo propertyInfo in typeof(DataResponse).GetProperties())
{
aggrResponseType.GetProperty(propertyInfo.Name).SetValue(aggrResponse, lst.Sum(x => (int)responseType.GetProperty(propertyInfo.Name).GetValue(x)));
}
return aggrResponse;
}
public static DataAggragationResponse DoAggregation(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataAggragationResponse aggrResponse = new DataAggragationResponse();
aggrResponse.Stuff = lst.Sum(x => x.Stuff);
aggrResponse.Stuff2 = lst.Sum(x => x.Stuff2);
return aggrResponse;
}
}
public class DataResponse
{
public int Stuff { get; set; }
public int Stuff2 { get; set; }
}
public class DataAggragationResponse
{
public int Stuff { get; set; }
public int Stuff2 { get; set; }
}
But, as a suggestion, if you want to go with this approach, its better if you can cache all the reflection invokes you're making as they are costly. And the 90's approach would still win in benchmark. Like the example above would benchmark like the following with the simple StopWatch.
1.8193
0.4476
Press any key to continue . . .
The first one is the execution time of DoAggregationReflection and the last one is the execution time of DoAggregation. You can optimize the reflection one as much as you want but I think it would still fail to compete with the basic one.
Sometime's the 90's are way better. ;) Although you'd still use LINQ to do the actual summation so that's not that 90's anymore as LINQ was born in 2007 according to wikipedia.
Hopefully this can help you. I wish I had kept the SO link to the question I pulled this from a while ago. Sorry to the original poster for not mentioning his/her name.
using System.Reflection;
public static Dictionary<string, string> GetPropertiesValue(object o)
{
Dictionary<string, string> PropertiesDictionaryToReturn = new Dictionary<string, string>();
foreach (MemberInfo itemMemberInfo in o.GetType().GetMembers())
{
if (itemMemberInfo.MemberType == MemberTypes.Property)
{
//object PropValue = GetPropertyValue(OPSOP, item.Name);
//string itemProperty = itemMemberInfo.Name;
//string itemPropertyValue = o.GetType().GetProperty(itemMemberInfo.Name).GetValue(o, null).ToString();
//Console.WriteLine(itemProperty + " : " + itemPropertyValue);
PropertiesDictionaryToReturn.Add(itemMemberInfo.Name, o.GetType().GetProperty(itemMemberInfo.Name).GetValue(o, null).ToString());
}
}
return PropertiesDictionaryToReturn;
}
It's not exactly what you need but, I think you could adapt it.
I would rather take a different approach. I would dynamically build and compile (once) something like this:
Func<DataContainerResponse, DataResponse, DataContainerResponse> aggregateFunc =
(result, item) =>
{
result.Prop1 += item.Prop1;
result.Prop2 += item.Prop2;
...
result.PropN += item.PropN;
return result;
}
(if you wonder why the signature is like the above, the answer is - because it can be used directly for the following Aggregate overload).
Here is how it can be done:
static readonly Func<DataContainerResponse, DataResponse, DataContainerResponse>
AggregateFunc = BuildAggregateFunc();
static Func<DataContainerResponse, DataResponse, DataContainerResponse> BuildAggregateFunc()
{
var result = Expression.Parameter(typeof(DataContainerResponse), "result");
var item = Expression.Parameter(typeof(DataResponse), "item");
var propertyTypes = new HashSet<Type> { typeof(decimal), typeof(int) };
var statements = item.Type.GetProperties()
.Where(p => propertyTypes.Contains(p.PropertyType))
.Select(p => Expression.AddAssign(
Expression.Property(result, p.Name),
Expression.Property(item, p)));
var body = Expression.Block(statements
.Concat(new Expression[] { result }));
var lambda = Expression.Lambda<Func<DataContainerResponse, DataResponse, DataContainerResponse>>(
body, result, item);
return lambda.Compile();
}
and the usage is simple:
public DataContainerResponse DoAggregation(List<DataResponse> source)
{
return source.Aggregate(new DataContainerResponse(), AggregateFunc);
}

EF 6 'Bulk' insert with FK relationed models

I am trying to bulk insert using EF (model first) on two tables that have a FK relationship (one to many). The code below properly inserts all of the Challenge entries but only inserts the X amount Shortages one time. My expectation... I have 10 shortages with 2 challenges. I should receive 2 challenge entries and 20 shortage entries. I am only seeing 10 challenge entries for the first shortage inserted. (the code below is simplified)
//class for cloning Shortage collection
public class ShortageCollection : Collection<Shortage>
{
public ShortageCollection(IList<Shortage> source) : base(source) { }
public ShortageCollection() { }
public ShortageCollection Clone()
{
return Clone(this);
}
public static ShortageCollection Clone(ShortageCollection shortage)
{
var res = new ShortageCollection();
foreach (var s in shortage)
{
res.Add(s.Clone());
}
}
}
public class Shortage : StandardDB.Shortage
{
public Shortage Clone()
{
return new Shortage()
{
PART_NUMBER = this.PART_NUMBER,
Note = this.Note,
Qty = this.Qty,
ResponseMachine = this.ResponseMachine
};
}
}
public void CreateChallenge()
{
var JSONJobs = new JavaScriptSerializer().Deserialize<string[]>(Jobs);
var JSONParts = new JavaScriptSerializer().Deserialize<ChallengePartsList[]>(Parts);
using (ARTEntities art = new ARTEntities())
{
art.Configuration.AutoDetectChangesEnabled = false;
art.Configuration.ValidateOnSaveEnabled = false;
ShortageCollection sColl = new ShortageCollection();
foreach(var part in JSONParts)
{
Shortage s = new Shortage()
{
PART_NUMBER = part.Invid,
Note = Challenge,
Qty = part.Qty,
ResponseMachine = ResponseMachine
};
sColl.Add(s);
}
foreach (var job in JSONJobs) {
Challenge c = new Challenge()
{
InitiatorORG = Org,
TypeID = TypeID,
DISCRETE_JOB = job,
InitiatorPERSON_ID = InitiatorPersonID,
InitiatedDate = datenow,
Challenge1 = Challenge,
ChampionGroupID = ChampionGroupID,
StatusID = StatusID,
InitiatorGroupID = InitiatorGroupID,
DivisionID = DivisionID,
Shortages = sColl.Clone()
};
art.Challenges.Add(c);
}
art.SaveChanges();
}
}
you are only creating 10 Shortages in memory. Your Clone method is a shallow clone, and doesn't go through and clone every object. Therefore you have 2 lists with 10 identical items (each pair of items point to exactly same memory reference).
What you need to do is DeepClone which is something like the following:
public static ShortageCollection Clone(ShortageCollection shortage)
{
var res = new ShortageCollection();
foreach(var s in shortage) {
res.Add( s.Clone() );
}
return res;
}
And in your shortage class:
public class Shortage
{
public Shortage Clone()
{
return new Shortage()
{
SomeProp = this.SomeProp,
SomeOtherProp = this.SomeOtherProp
}
}
}
Be aware that if inside shortage any of those objects point to another entity, each pair will point to the same entity.
Search for DeepClone for more info

Preserve value of an object instance

I think this problem is a value/reference instantiate problem in Object Oriented languages like C#. But I'm newbie and I don't know how to turnaround.
I have a method with this piece of code:
List<CSpropr> listActionpropr = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
// For each record...
foreach (CSpropr instance in listActionpropr)
{
instance.ValName = "John";
instance.ValPhone = 323234232;
instance.update(); // This make and UPDATE of the record in DB
}
Later in the same method, I want to use the first version of listActionpropr, before the update. To make a sort of rollback. But when I iterate the listActionpropr variable I get the list of records with the changes in Name and Phone values.
For example:
foreach (CSApropr instance1 in listActionpropr )
{
instance1.update();
}
Is there an elegantly way to preserve the value without create a new search to other variable? Like this:
List<CSpropr> listActionpropr = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
List<CSpropr> preservedList = CSpropr.searchList(actionCondition); // Get a list of all records of table PROPR for the 'actioncondition' specified
// For each record...
foreach (CSpropr instance in listActionpropr)
{
instance.ValName = "John";
instance.ValPhone = 323234232;
instance.update(); // This make and UPDATE of the record in DB
}
....
foreach (CSApropr instance1 in preservedList )
{
instance1.update();
}
I would use DeepClone in this case with serialization as per this answer : https://stackoverflow.com/a/519512/841467
My fast implementation in LinqPad was :
void Main()
{
List<CSpropr> listActionpropr = CSpropr.searchList("act"); // Get a list of all records of table PROPR for the 'actioncondition' specified
List<CSpropr> preservedList = listActionpropr.DeepCopy(); // Get a list of all records of table PROPR for the 'actioncondition' specified
// For each record...
foreach (CSpropr instance in listActionpropr)
{
instance.ValName = "John";
instance.ValPhone = 323234232;
instance.update(); // This make and UPDATE of the record in DB
}
foreach (CSpropr instance1 in preservedList )
{
instance1.update();
}
}
// Define other methods and classes here
[Serializable]
public class CSpropr {
public string ValName {get;set;}
public int ValPhone {get;set;}
public void update() {
ValName.Dump();
ValPhone.Dump();
}
public static List<CSpropr> searchList(string act) {
return new List<CSpropr> { new CSpropr {ValName = "First", ValPhone = 4444} , new CSpropr {ValName = "First", ValPhone = 4444 }};
}
}
public static class GenericCopier
{
public static T DeepCopy<T>(this T original) where T : class
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, original);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
result is :
John
323234232
John
323234232
First
4444
First
4444
What you are talking about is close to transactions in c# objects. There is no inherent support for object transactions in c#.
Microsoft did start a project on STM (Software Transactional Memory) to support the transaction features for .NET objects. However, the project has already been retired for various reasons.
So, all you can do now is have a separate object for original list.
You could either DeepCopy the object with the some helper method as described in other answer or implement IClonable interface for you object, which does the deep copy for you. An example would be :
public class Person : ICloneable
{
public string LastName { get; set; }
public string FirstName { get; set; }
public Address PersonAddress { get; set; }
public object Clone()
{
Person newPerson = (Person)this.MemberwiseClone();
newPerson.PersonAddress = (Address)this.PersonAddress.Clone();
return newPerson;
}
}
The Address class uses MemberwiseClone to make a copy of itself.
public class Address : ICloneable
{
public int HouseNumber { get; set; }
public string StreetName { get; set; }
public object Clone()
{
return this.MemberwiseClone();
}
}
Cloning a Person:
Person herClone = (Person)emilyBronte.Clone();
The example is taken from one of the excellent blogs in internet for c#
Of course, when I meant object, it's applicable to List<objects> as well.
Basically you want two identical lists but if you change one element of the list1 it should not affect the same element of list2 .
You can do this in various ways: eg using serialization.
Following code will show my way:
Module Module1
Sub Main()
Dim listone As New List(Of demo) 'first list
Dim listwo As New List(Of demo) ' second list
Dim var1 As Integer
Dim var2 As String
Dim obj As New demo() 'first object created in list-1
obj.id = 1
obj.name = "sumi"
listone.Add(obj) 'first object inserted in the list-1
Dim obj1 As New demo()
obj1.id = 3
obj1.name = "more"
listone.Add(obj1) 'Second object inserted in the list-1
For Each w In listone
Dim obj3 As New demo()
var1 = w.id
obj3.id = var1
var2 = w.name
obj3.name = var2
listwo.Add(obj3) 'looping all objects of list-1 and adding them in list-2 .Hence making both lists identical
Next
For Each p In listone 'making change in the list-1 and this change should not be refelected in list-2
If (p.id = 1) Then
p.id = 5
End If
Next
For Each z In listone
Console.WriteLine(z.id)
Console.WriteLine(z.name)
Next
For Each q In listwo
Console.WriteLine(q.id)
Console.WriteLine(q.name)
Next
Console.ReadLine()
End Sub
Class demo
Public name As String
Public id As Integer
End Class
End Module
Output:
5
sumi
3
more
1
sumi
3
more
Hence cloned list remains unaffected irrespective of changes in original list
You can store your first list object like that:
List<CSpropr> StoreFirstListInTemp(List<CSpropr> listActionpropr)
{
List<CSpropr> temp = new List<CSpropr>();
temp.AddRange(listActionpropr);
return temp;
}

Problem with circular reference list

I have a small problem and I would like to get your opinion.
I'm dealing with documents than can reference other documents. Starting from any document, I need to get the id of all the documents this document references. The problem is that the circular references are allowed so if A ref B ref C, again C can ref A and I get in the loop.
How can I solve this problem in C#?
An small example:
Let suppose that this is a class that represents a document:
public class Document
{
public Document(int id)
{
this.ID = id;
}
private int m_ID;
public int ID
{
get { return m_ID; }
set { m_ID = value; }
}
private List<Document> m_Children = new List<Document>();
public List<Document> Children
{
get { return m_Children; }
set { m_Children = value; }
}
private List<Document> m_Parent = new List<Document>();
public List<Document> Parent
{
get { return m_Parent; }
set { m_Parent = value; }
}
public Document AddChild(Document child)
{
child.Parent.Add(this);
this.Children.Add(child);
return child;
}
public Document AddChild(int child)
{
Document d = new Document(child);
return AddChild(d);
}
}
Now let's create a Document class that has some references:
public static Document CreateReferences()
{
Document d = new Document(1);
Document temp = d.AddChild(2);
for (int i = 3; i < 6; i++)
{
temp = temp.AddChild(i);
}
temp.AddChild(d);
return d;
}
Now I need to implement a method in Document class like
public List<int> GetReferencedDocuments()
{ }
What is the best way to do that? Any specific algorithm can be implemented?
Any suggestion is well accepted!
Thanks
Any tree-traversal algorithm would be fine.
As well as a list of docs you're going to build up, maintain a queue of documents you've yet to check, add the first document to that list.
Then, while the queue isn't empty, get the next doc, if it's not already in your list, then add it, and add all referenced docs to your queue.
List<Document> FoundDocs = new List<Documents();
Queue<Document> DocsToSearch = new Queue<Document>();
DocsToSearch.Enqueue(StartDoc);
while(DocsToSearch.Count != 0)
{
Document Doc = DocsToSearch.Dequeue();
if(!FoundDocs.Contains(Doc))
{
FoundDocs.Add(Doc);
foreach(var ChildDoc in Doc.Children)
{
DocsToSearch.Enqueue(ChildDoc);
}
}
}
The best way is to do a depth first search or a breadth first search
There are two main approaches to resolving this sort of recursive search on recursive data: marking or recording.
Marking: every time you list a document, flag it as viewed. Do not process flagged documents.
So your GetReferenceDocuments would look a little like this:
GetReferencedDocuments(startpoint)
if(startpoint.flagged) return null
startpoint.flag
new list result =startpoint
foreach(subdocument in
documents.children)
result.append(getreferenceddocuments(subdocuments))//
if not null
Recording: a similar approach, but the flag indicators are replaced by a list of already referenced documents ( a separate list of ids maybe ), and the flag check is a search on this list for this document.
Either way will work, depending on your objects, size and scale. If you cannot change the document objects, you will have to list them. If you have, potentially, 1M documents in your scan, you do not want to list them.
Example implementation:
public List<int> GetReferencedDocuments()
{
var referencedIds = new List<int>();
var queue = new Queue<Document>(this);
while (queue.Count > 0)
{
var newDocuments = queue.Dequeue().Children
.Where(d => !referencedIds.Contains(d.ID))
foreach (Document newDocument in newDocuments)
{
queue.Enqueue(newDocument);
referencedIds.Add(newDocument.ID);
}
}
return referencedIds;
}

Categories

Resources