Print Queue implementation by linked list class C# - c#

I am a student and not an advance developer. I faced an issue regarding to construct a program. I have tried a few things, but still could not solved it. The question came from an exam.
Apologize for this post, because my previous two posts were not accepted well. This time I tried, but got no success still for my posted issue.
Question as follows
a) Write a class named Document to represent a document node in a print queue. It should contain a method name that returns the subject of the document and an abstract method called type that returns the document type. Derive from a document class two concrete classes named word document and pdf document.
b) Another class named Print Queue that is implemented as a linked list of Document objects. Print Queue should have a method named Push for adding a document to the end of the list, a method named pop for removing the first document from the list, and a method named DisplayContents for listing all of the documents in the Print Queue, reporting both the Name and Type of each element Print Queue should not use any standard library classes it should be own implementation of linked list.
Here I have coded it that way:
class PrintQueue
{
private Document head;
private int size;
public int Count
{
get
{
return size;
}
}
public void Push(Object data)
{
Document toAdd = new Document();
toAdd.data = data;
Document current = head;
while (current.Next != null)
{
current = current.Next;
}
current.Next = toAdd;
size++;
}
public bool Pop()
{
Document tempNode = head;
Document lastNode = null;
int count = 0;
if (size > 0)
{
while (tempNode != null)
{
if (count == size - 1)
{
lastNode.Next = tempNode.Next;
return true;
}
count++;
lastNode = tempNode;
tempNode = tempNode.Next;
}
}
return false;
}
}
public abstract class Document
{
public Document Next;
public Object data;
public string Subject { get; set; }
public abstract string type();
}
public class WordDocument : Document
{
public override string type()
{
return "docx";
}
}
public class PdfDocument : Document
{
public override string type()
{
return "pdf";
}
}
This line causes the problem Document toAdd = new Document();
But I still could not find a way for DisplayContents function which will list all of the documents in the Print Queue, reporting both the Name and Type of each element Print Queue.
Could anyone review my code and help me to get it rectified as per the question. Highlight those areas where I have made mistakes.
Thanks
UPDATE
the 2 questions i was trying to solve as follows
Question 1 : Write a class named Document to represent a document node in a print queue. it should contain a method name that returns the subject of the document and an abstract method called type that returns the document type . from document class derive two concrete classes named word document and pdf document.
Question 2 : Another class named Print Queue that is implemented as a linked list of Document objects. Print Queue should have a method named Push for adding a document to the end of the list, a method named pop for removing the first document from the list , and a method named DisplayContents for listing all of the documents in the Print Queue, reporting both the Name and Type of each element Print Queue should not use any standard library classes it should be own implementation of linked list.
here i like to post whatever at last i achieved. so please see the code and question let me know is it correct as per the 2 question pasted here.
see the latest code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PrintQueueDemo
{
class PrintQueue
{
Node head;
Node tail;
/// <summary>
/// add a document to the print queue
/// </summary>
/// <param name="strType">document type</param>
/// <param name="strName">docunent name</param>
public void Push(string strType, string strName)
{
if (head == null)
{
head = new Node(strType, strName);
tail = head;
}
else
{
Node node = new Node(strType, strName);
tail.setNext(node);
tail = node;
}
}
/// <summary>
/// pop a document from the queue
/// </summary>
/// <returns>null if printqueue is empty, else document</returns>
public Node.Document Pop()
{
if (head == null)
return null;
Node.Document doc = new Node.Document(head.document.Type, head.document.Name);
head = head.nextnode;
if (head == null)
tail = null;
return doc;
}
/// <summary>
/// get the current content of the queue
/// </summary>
/// <returns>string with current content (line per entry)</returns>
public string DisplayContents()
{
string content = "";
Node node = head;
if (node == null)
return "PrintQueue is empty";
content = node.document.Name + ": " + node.document.Type;
while (node.nextnode != null)
{
node = node.nextnode;
content += "\r\n" + node.document.Name + ": " + node.document.Type;
}
return content;
}
public class Node
{
public Document document { get; private set; }
public Node nextnode { get; private set; }
public Node(string strType, string strName)
{
document = new Document(strType, strName);
}
public void setNext(Node node)
{
nextnode = node;
}
public class Document
{
public string Type { get; private set; }
public string Name { get; private set; }
public Document(string strType, string strName)
{
Name = strName;
Type = strType;
}
}
}
}
}
PrintQueue pq = new PrintQueue();
pq.Push("cpp", "main.cpp");
pq.Push("c#", "main.cs");
pq.Push("c", "main.c");
pq.Push("h", "myinclude.h");
Console.WriteLine(pq.DisplayContents());
Console.WriteLine("===");
PrintQueue.Node.Document doc;
doc = pq.Pop();
Console.WriteLine("{0}: {1}", doc.Name, doc.Type);
doc = pq.Pop();
Console.WriteLine("{0}: {1}", doc.Name, doc.Type);
doc = pq.Pop();
Console.WriteLine("{0}: {1}", doc.Name, doc.Type);
doc = pq.Pop();
Console.WriteLine("{0}: {1}", doc.Name, doc.Type);
Console.WriteLine("===");
Console.WriteLine(pq.DisplayContents());
Console.WriteLine("===");
pq.Push("xls", "workbook.xls");
Console.WriteLine(pq.DisplayContents());
just tell me the above code will be accepted as per 2 question which i pasted at top. thanks

Related

singly linked list in c# using extension method

why extension method does not return the modified node in Insertion operations.
But It Working Fine at the time of Linked list Creation.
Extension method should return the modified Node.
What is the perfect way to do this.
IS extension method good in performance
Code follows
public class Node
{
public Object Data { get; set; }
public Node NextNode { get; set; }
}
public static class ListOperations
{
public static void CreateLinkedList(this Node node, Object data)
{
if (node.Data == null)
{
node.Data = data;
}
else
{
Node newnode = new Node();
newnode.Data = data;
Node current = new Node();
current = node;
while (current.NextNode != null)
{
current = current.NextNode;
}
current.NextNode = newnode;
node = current;
}
}
public static void InsertNode(this Node node1, Object data, int position)
{
Node newnode = new Node();
newnode.Data = data;
if (position == 1)
{
newnode.NextNode = node1;
node1 = newnode;
}
}
}
class Program
{
static void Main(string[] args)
{
Node node = new Node();
//random Singly LinkedList
node.CreateLinkedList(10);
node.CreateLinkedList(11);
node.CreateLinkedList(12);
node.CreateLinkedList(13);
node.CreateLinkedList(14);
node.CreateLinkedList(15);
node.InsertNode(20, 1);// this method does not return node value what is inserted.
}
}
There are many things wrong with your code, and we can deal with them later. But let's answer your questions first. I will be a bit literal and direct, since I can't assume why you have done it the way it is done.
why extension method does not return the modified node in Insertion operations.
Since your method doesn't return anything
But It Working Fine at the time of Linked list Creation.
Yes, since that code doesn't ever modify the this Node node parameter
Extension method should return the modified Node.
Only if you actually return any data from the method!
What is the perfect way to do this.
See below
IS extension method good in performance
Extension method compared with what? Compared with member method written similarly, there should really be no performance difference in the cases relevant to your example
Perfect way to do it:
So first things first: There is no need to write an extension method here. Why wouldn't you write a regular member method? Extensions are usually done when the class you want to add the functionality is not directly available for you to edit, typically as the code belongs to a third party
Second, you don't quite seem to understand the references and how the pass-by-value works. First let me post a better code, and then explain it
public class Node {
public object Data { get; set; }
public Node NextNode { get; set; }
public Node(object data) {
Data = data;
}
public Node AppendNode(object data) {
var newNode = new Node(data);
var current = this;
while (current.NextNode != null)
current = current.NextNode;
current.NextNode = newNode;
return newNode;
}
public Node SetFirstNode(object data) {
return new Node(data) { NextNode = this };
}
}
class Program {
static void Main(string[] args) {
var linkedList = new Node(10);
linkedList.AppendNode(11);
linkedList.AppendNode(12);
linkedList.AppendNode(13);
linkedList.AppendNode(14);
linkedList.AppendNode(15);
linkedList = linkedList.SetFirstNode(20);
}
}
The important things to notice from the perspective of your main question (why the insert did not work) is that the method SetFirstNode actually returns the newly created node and in Main, we re-assign the linkedlist as such linkedList = linkedList.SetFirstNode(20);
Now, you can actually write a static method and pass by ref the linkedlist, but that is not a good practice, in my opinion. Nevertheless, the code would look like below
public static class ListOperations {
public static void InsertNode(ref Node linkedList, object data) {
linkedList = new Node(data) { NextNode = linkedList };
}
}
Among other things to notice, I am calling the node object as linkedList, CreateLinkedList as AppendNode and InsertNode as SetFirstNode on purpose, so you can understand the code better.
Below is the same code with generic argument instead of object Data and using a proper InsertNode method
public class Node<T> {
public T Data { get; set; }
public Node<T> Next { get; set; }
public override string ToString() {
return Data.ToString();
}
public Node(T data) {
Data = data;
}
public Node<T> AppendNode(T data) {
var newNode = new Node<T>(data);
var current = this;
while (current.Next != null)
current = current.Next;
current.Next = newNode;
return newNode;
}
/// <summary>
/// Inserts a new node into the linkedlist as the desired position
/// </summary>
/// <param name="position">0-based index for the final position of new node</param>
/// <param name="newNode">The newly created node containing data</param>
/// <returns>returns the first node of the linkedlist</returns>
public Node<T> InsertNode(T data, int position, out Node<T> newNode) {
var current = this;
position--;
newNode = new Node<T>(data);
if (position < 0) {
newNode.Next = current;
return newNode;
}
for (int i = 0; i < position; ++i)
current = current.Next;
newNode.Next = current.Next;
current.Next = newNode;
return this;
}
}
class Program {
static void Main(string[] args) {
var linkedList = new Node<int>(10);
linkedList.AppendNode(11);
linkedList.AppendNode(12);
linkedList.AppendNode(13);
linkedList.AppendNode(14);
linkedList.AppendNode(15);
linkedList = linkedList.InsertNode(20, 0, out var newNode);
}
}

Time difference between iterating over a file tree created during a scan vs. one created by duplicating the scanned tree

(Title and question have been significantly changed, as the none of the important parts ended up being relevant to the problem)
I have a generated file tree of a hard drive, and I'm creating a function to highlight every instance of an extension in the file tree. For some reason iterating over any duplicate file tree other than the one created during the scan can take at least twice as long. Please note that I am not trying to iterate over the file tree during the scan.
What exactly is causing the slowdown? List<FileNode> seems to be the culprit, but I'm not sure what internal mechanism is at fault.
I've created a gist with 4 files to enumerate a file tree and show the inconsistencies in iteration times: FileTreeIterationSpeedTest
Performance for drive with 2m files and 200k directories:
Output from gist:
Scanning...
RAM Used: 300.6 MB
Bytes: 443.7 GB
Files: 1,925,131
Folders: 156,311
Progress: 100.0%
Duration: 00:00:17
Scan complete!
Duplicating file tree...
Duplication complete!
RAM Used: 311.4 MB
Iterating: 1000
Scanned Tree: 00:03.857
Duped Tree: 00:01.409
Duped Tree is 173.6% faster
Press any key to continue...
Relevant Code from FileNode.cs:
public class FileNode {
public enum FileNodeType {
Root,
Directory,
FileCollection,
File,
}
private readonly List<FileNode> children = new List<FileNode>();
private FileNode fileCollection;
public FileNode Parent { get; private set; }
public FileNodeType Type { get; }
public long Size { get; private set; }
public string Extension { get; } = string.Empty;
public string Name { get; }
// File Collection
private FileNode() {
Type = FileNodeType.FileCollection;
Name = "<Files>";
}
// Root Node
public FileNode(string drivePath) {
Type = FileNodeType.Root;
Name = drivePath;
}
// File or Directory Node
public FileNode(Win32FindData find) {
if (!find.IsDirectory) {
Type = FileNodeType.File;
Extension = Path.GetExtension(find.cFileName);
}
else {
Type = FileNodeType.Directory;
}
Name = find.cFileName;
Size = find.Size;
}
// Duplicate Tree \w Parent
public FileNode(FileNode root) : this(root, null) {
}
// Duplicate Tree \w Parent
private FileNode(FileNode file, FileNode parent) {
Parent = parent;
Type = file.Type;
Size = file.Size;
Extension = file.Extension;
Name = file.Name;
int count = file.children.Count;
children = new List<FileNode>(count);
for (int i = 0; i < count; i++)
children.Add(new FileNode(file[i], this));
}
public void AddChild(FileNode item) {
if (item.Type == FileNodeType.File && Type != FileNodeType.FileCollection) {
if (fileCollection == null)
fileCollection = new FileNode();
fileCollection.AddChild(item);
}
else {
children.Add(item);
item.Parent = this;
}
}
public bool IsLeaf => children.Count == 0;
public int Count => children.Count;
public FileNode this[int index] => children[index];
}

Display contents of List<T> in a .txt file via a class

I am fairly new to programming so please have some patience whilst I try and explain! I've been looking over this specific method for a while now and it may just be simple, however, I want to display all the items I have in a listbox to a .txt file. However it just displays the following in my text file:
System.Collections.Generic.List`1[QA.OrderItem]
My save method is in one class and I have an overriden .ToString in a separate class that is correctly display the list items in the listbox instead of:
System.Collections.Generic.BlahBlahBlah
I though I could use a foreach loop (which will use my overriden .ToString) that will iterate through my list and write to the text file, however, this didn't seem to work!
Any help would be much appreciated and thank you for your time (please see all necessary code below).
Save method in the ShoppingBasket class:
public bool SaveBasket(string fileName)
{
// A string that specifies a subfolder to be made.
string path = #"C:\Users\Public\BasketSaves";
// Create the subfolder.
if (!System.IO.Directory.Exists(path)) {
System.IO.Directory.CreateDirectory(path);
}
// Combine the filename to the end of the path.
path = System.IO.Path.Combine(path, fileName + ".txt");
// Check that the file doesn't exsist. If it doesn't, create
// the file and write the list box to it.
if (!System.IO.File.Exists(path)) {
System.IO.File.WriteAllText(path, OrderItems.ToString());
// Return true when the file doesn't exsist.
return true;
}
else {
// Return false when the file exsists.
return false;
}
}
Overriden .ToString() in OrderItem class:
public override string ToString()
{
return string.Format("{0}\t {1}\t {2}",ProductName, LatestPrice, Quantity);
}
EDIT
OrderItem class:
public class OrderItem : IOrderItem {
public OrderItem(string productName, decimal latestPrice):this(productName, latestPrice, 1) {
}
public OrderItem(string productName, decimal latestPrice, int quantity) {
this.ProductName = productName;
this.LatestPrice = latestPrice;
this.Quantity = quantity;
}
/******************* Properties *******************/
public string ProductName { get; private set; }
public decimal LatestPrice { get; private set; }
public decimal TotalOrder { get; private set; }
public int Quantity { get; private set; }
/*************************************************/
/// <summary>
/// Add multiple items to a basket with a new latest price
/// </summary>
/// <param name="latestPrice">Takes the latest price of a specified product</param>
/// <param name="numberOfItems">Takes the amount you wish to add</param>
/// <returns>The quantity</returns>
public int AddItems(decimal latestPrice, int numberOfItems) {
if (numberOfItems <= 0 || latestPrice < 0) {
throw new ArgumentOutOfRangeException();
}
LatestPrice = latestPrice;
Quantity += numberOfItems;
// Return the Quantity value.
return Quantity;
}
/// <summary>
/// Add multiple items (no price change functionality)
/// </summary>
/// <param name="numberOfItems">Takes the amount you wish to add</param>
/// <returns>The quantity</returns>
public int AddItems(int numberOfItems) {
if (numberOfItems <= 0) {
throw new ArgumentOutOfRangeException();
}
return Quantity += numberOfItems;
}
/// <summary>
/// Adds an item with incrementation
/// </summary>
/// <returns>Incrementation then the Quantity value</returns>
public int AddItem() {
return ++Quantity;
}
/// <summary>
/// Add multiple items
/// </summary>
/// <param name="numberOfItems">Takes the amount you wish to subtract</param>
/// <returns>The quantity</returns>
public int RemoveItems(int numberOfItems) {
Quantity -= numberOfItems;
if (numberOfItems <= 0) {
throw new ArgumentOutOfRangeException();
}
if (Quantity < 0) {
return Quantity = 0;
}
// Return the Quantity if the above is false.
return Quantity;
}
/// <summary>
/// Adds an item with decrementation
/// </summary>
/// <returns>Decrement then the Quantity value</returns>
public int RemoveItem() {
return --Quantity;
}
public override string ToString()
{
return string.Format("{0}\t {1}\t {2}",ProductName, LatestPrice, Quantity);
}
}
ShoppingBasket class:
public class ShoppingBasket : IShoppingBasket
{
public ShoppingBasket()
{
OrderItems = new List<OrderItem>();
}
public List<OrderItem> OrderItems { get; private set; }
public int NumberOfProducts {
get {
return OrderItems.Count();
}
}
public decimal BasketTotal { get; private set; }
public int NumberOfItems {
get {
return OrderItems.Count();
}
}
public void AddProduct(string productName, decimal latestValue, int quantity = 1) {
OrderItems.Add(new OrderItem(productName, latestValue));
}
public void RemoveProducts(string productName, int quantity = 1) {
OrderItems.Remove(new OrderItem (productName, quantity));
}
public void ClearBasket() {
OrderItems.Clear();
}
public bool SaveBasket(string fileName)
{
// A string that specifies a subfolder to be made.
string path = #"C:\Users\Public\BasketSaves";
// Create the subfolder.
if (!System.IO.Directory.Exists(path)) {
System.IO.Directory.CreateDirectory(path);
}
// Combine the filename to the end of the path.
path = System.IO.Path.Combine(path, fileName + ".txt");
// Check that the file doesn't exsist. If it doesn't, create
// the file and write the list box to it.
if (!System.IO.File.Exists(path)) {
System.IO.File.WriteAllText(path, OrderItems.ToString());
// Return true when the file doesn't exsist.
return true;
}
else {
// Return false when the file exsists.
return false;
}
}
}
This allows the user to add an item to the basket:
private void btnAdd_Click(object sender, EventArgs e) {
OrderItem item = new OrderItem(txtName.Text, Convert.ToDecimal(txtLatestPrice.Text), Convert.ToInt32(txtQuantity.Value));
lbBasket.Items.Add(item.ToString());
}
You are calling WriteAllText in a loop and thus replacing the whole file with the current item at each iteration.
Every type in .NET inherits the method ToString() from System.Object alias object. The default implementation for ToString() is to display the type name. Types other than object must override ToString() if they want to provide another behavior.
OrderItems is a collection (List<T>). Collections usually don't override ToString() and do therefore display their type name when converted to strings. You have to iterate over a collection and to concatenate the string representation of its items in order to get an adequate string representation of the collection's content.
Since you have overridden ToString() in OrderItem you can convert the List<OrderItem> like this:
// Until .NET 3.5
string s = String.Join("\r\n", OrderItems.Select(o => o.ToString()).ToArray());
// From .NET 4.0
string s = String.Join("\r\n", OrderItems.Select(o => o.ToString()));
Starting with .NET Framework 4.0 File.WriteAllLines has an overloaded version accepting an enumeration as well. This has the advantage over using a ToArray() that an enumeration is evaluated lazily and that the result must not be kept in memory as a whole.
File.WriteAllLines(filename, OrderItems.Select(o => o.ToString()));
Or you can loop over the list and write the items one by one to the file.
UPDATE
Since you converted the items to strings when adding them to the listbox (what is not really necessary since the list box does it automatically when displaying the items) you can also write the file like this:
File.WriteAllLines(filename, lbBasket.Cast<string>());
You still need the cast, since the ObjectCollection used by ListBox is not a generic collection, i.e. it implements IEnumerable but not IEnumerable<T>.
One approach I would suggest is to loop through your collection writing out each item.
var sb = new StringBuilder();
foreach(var item in OrderItems)
{
// send item to the text file or build a string using StringBuilder
sb.AppendLine(item.ToString());
}
// now dump sb.ToString() to your text file
MSDN has a great example of how to write text to a file:
Write text to a file
Try the following
System.IO.File.WriteAllLines(path, (IEnumerable<string>)OrderItems.Items);
It's off the top of my head so not sure if it'll work.
Otherwise just do
StringBuilder builder = new StringBuilder();
using(System.IO.StreamWriter writer = new System.IO.StreamWriter(path)
{
foreach(string item in OrderItems.Items)
{
builder.append(string.format("{0}{1}", item, Environment.NewLine));
}
writer.write(builder.ToString());
}
Your loop didnt work because the WriteAllText methods writes text per file.
If you really want to use this method concate all the values into one string
before writing. Or use StreamWriter instead.
Currently you are writing
OrderItems.ToString():
To the file and the will not loop on items
It will just give a systems message of what OrderItems is
In your other loop code you looped on the file
Better to build up the content and then write once
Try this
if(OrderItems != null && OrderItems.Count > 0)
{
StringBuilder sb = new StringBuilder();
foreach (var item in OrderItems)
{
Debug.WriteLine(item.ToString());
sb.AppendLine(item.ToString());
}
System.IO.File.WriteAllText(path, sb.ToString());
}
else
{
Debug.WriteLine("Nothing to write.");
System.IO.File.WriteAllText(path, "No items");
}

Selecting a node from within an XML document?

So I have an xmlDocument and I need to check to see if a credit score was appended. To do this I am using xmlNodes.SelectSingleNode and then checking the innerText.
My issue is this: one of the nodes has an ID field in the actual node name. So I think C# is interpreting that as part of the node name.
public void DeperPostAppend()
{
DirectoryInfo CompDir = new DirectoryInfo(FilePrep.CompletedDirectory);
foreach (FileInfo File in CompDir.GetFiles())
{
// Load xml documents for sorting
XmlDocument xmlDoc = new XmlDocument();
try
{
xmlDoc.Load(File.FullName);
}
catch
{
if (File.Extension != ".xml")
return;
}
//XmlNode auto = xmlDoc.SelectSingleNode("//ACORD//InsuranceSvcRq//PersAutoPolicyQuoteInqRq");
XmlNode home = xmlDoc.SelectSingleNode("//ACORD//InsuranceSvcRq//HomePolicyQuoteInqRq");
XmlNode creditAuto = xmlDoc.SelectSingleNode("//ACORD//InsuranceSvcRq//PersAutoPolicyQuoteInqRq//PersPolicy//CreditScoreInfo//CreditScore");
XmlNode creditHome = xmlDoc.SelectSingleNode("//ACORD//InsuranceSvcRq//HomePolicyQuoteInqRq//PersPolicy//CreditScoreInfo//CreditScore");
//if file is type home quote
if (File.Extension == ".xml" && creditHome != null)
{
if (creditHome.InnerText != "ERR" || creditHome.InnerText != "NOH")
{
DeperHome();
}
}
//If file is type Auto Quote
else if (File.Extension == ".xml" && creditAuto != null)
{
if (creditAuto.InnerText != "ERR" || creditAuto.InnerText != "NOH")
{
DeperAuto();
}
}
}
}//end DeperPostAppend
//ACORD//InsuranceSvcRq//HomePolicyQuoteInqRq//PersPolicy//CreditScoreInfo//CreditScore
PersPolicy is where the issue is. the node looks like this on the document.
<PersPolicy id="AE4562BEE086A92470D4">
I want to ignore the id portion due to the fact that it changes every document and i have thousands of docs to process.
I just decided to use classes to handle the nodes as follows
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
/// <summary>
///
/// </summary>
public class PersPolicy
{
XElement self;
public PersPolicy(XElement self) { this.self = self; }
public CreditScoreInfo CreditScoreInfo { get { return _CreditScoreInfo ?? (_CreditScoreInfo = new CreditScoreInfo(self.Element("CreditScoreInfo"))); } }
CreditScoreInfo _CreditScoreInfo;
public string PolicyNumber
{
get { return (string)self.Element("PolicyNumber"); }
set { self.Element("PolicyNumber").SetValue(value); }
}
}
/// <summary>
///
/// </summary>
public class CreditScoreInfo
{
XElement self;
public CreditScoreInfo(XElement self) { this.self = self; }
public string CreditScore
{
get { return (string)self.Element("CreditScore"); }
set { self.Element("CreditScore").SetValue(value); }
}
public string CreditScoreDt
{
get { return (string)self.Element("CreditScoreDt"); }
set { self.Element("CreditScoreDt").SetValue(value); }
}
}

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