In my C# .Net 4.0 composite pattern I want to have leafs that are generic. Most examples I found have a generic in the base node which propagates through the whole composite tree. I do not want that.
I have found the following solution (which I've stripped a bit to the essentials). An interface called INode which has two implementations. One called category which basically is a dictionary of INodes. It is a dictionary because I do not want duplicate leafs. The other implementation called ValueNode holds the information.
This allows for differently typed leaf nodes.
public interface INode
{
string Name { get; }
}
public class CategoryNode : INode
{
public CategoryNode(string name)
{
this.Name = name;
this.Children = new Dictionary<string, INode>();
}
public string Name { get; private set; }
public List<string> Keys
{
get { return this.Children.Keys.ToList(); }
}
private Dictionary<string, INode> Children { get; set; }
public INode this[string key]
{
get { return this.Children[key]; }
}
public void Add(INode node)
{
this.Children.Add(node.Name, node);
}
}
public class ValueNode<T> : INode
{
public ValueNode(
string name,
T defaultValue)
{
this.Name = name;
this.Value = this.Default = defaultValue;
}
public ValueNode(
string name,
T defaultValue)
{
this.Name = name;
this.Value = this.Default = defaultValue;
}
public T Default { get; private set; }
public T Value { get; set; }
public string Name { get; private set; }
}
Notice that I've made the children list private so nobody can remove nodes.
I am comfortable with this solution. However, the usage syntax it produces is a bit talkative. For example:
((this.root["category"] as CategoryNode)["leaf"] as ValueNode<int>).Value = (node as ValueNode<int>).Value;
While I had envisioned something like
this.root["category"]["leaf"] = node;
Does anybody have ideas for me to simplify the syntax?
How about adding an extension method to INode type ?
public static class INodeExtensions
{
public static void SetValue<T>(this INode node, string key, T v)
{
if(v is INode)
{
// category node set value
if(node is CategoryNode)
{
// convert and set value
}
else
{
throw new Exception("No children found.");
}
}
else
{
// value node set value
}
}
}
What about using a parameter array to specify the "path" to your leaf?
Optionally, there is another method in case you need to get a category node.
class CategoryNode : INode
{
public CategoryNode GetCategoryNode(params string[] path) {
CategoryNode cat = (CategoryNode)this.Children[path[0]];
for (int i = 1; i < path.Length; ++i) {
cat = (CategoryNode)cat.Children[path[i]];
}
return cat;
}
public ValueNode<T> GetLeafNode<T>(params string[] path) {
INode first = this.Children[path[0]];
if (path.Length == 1 && first is ValueNode<T>) return (ValueNode<T>)first;
CategoryNode cat = (CategoryNode)first;
for (int i = 1; i < path.Length - 1; ++i) {
cat = (CategoryNode)cat.Children[path[i]];
}
return (ValueNode<T>)cat.Children[path[path.Length-1]];
}
}
You use it like this:
var leafNode = root.GetLeafNode<int>("cat1", "cat2", "leaf");
// or
root.GetLeafNode<int>("cat1", "cat2", "leaf").Value = 1234;
The indexer is no longer needed.
I ended up with what Teddy proposed and also added a GetValue.
In addition, I put the indexer in the INode interface and just throw an exception when it is called on a value node. This way you can also use the this.root["category"]["leaf"] syntax.
You still must cast to a ValueNode<> if you want to access the value property though. But you can do this.root["category1"]["category2"].SetValue<int>("leaf", 42).
Related
I have created a custom Linked List , code given below .
Now trying to implement a sort (its what I told to do, not the best choice I know) , how we can do in best possible time complexity or the best approach
my custom linked list
my doubt is over the last node , in each phase of bubble sort should try to sort last and then start over from first node , how to handle the last node as its points to the first node
public class CustomCircularList<T> : ICollection<T>, IEnumerable<T>
{
Node<T> head = null;
Node<T> tail = null;
int count = 0;
readonly IEqualityComparer<T> comparer;
public int Count { get { return count; } }
public bool IsReadOnly { get { return false; } }
public void Add(T item)
{
this.AddLast(item);
}
AddLast...
}
}
my Node class has three properties
public T Value { get; private set; }
public Node<T> Next { get; set; }
public Node<T> Previous { get; set; }
I added IComparer to my class T like this and trying to work like below
public class Fund: IComparer<Fund>
{
public string fundname{ get; set; }
public int Compare([AllowNull] Fund x, [AllowNull] Fund y)
{
if (x == null || y == null)
{
return 0;
}
return x.fundname.CompareTo(y.fundname);
}
First of all, let me assume that Node<T> object has (at least) 2 standard properties: Value and Next:
public class Node<T> {
...
public T Value {get; set;}
public Node<T> Next {get;}
}
Since you have circular linked list,
tail.Next == head
we can enumerate all items except the last one as
for (Node<T> node = head; !ReferenceEquals(node.Next, head); node = node.Next) {
...
}
Just for reference, should we have a linked list (non circular), the loop would be (all we should do is to change head to null):
for (Node<T> node = head; !ReferenceEquals(node.Next, null); node = node.Next) {
...
}
The code can be
public void BubbleSort(IComparer<T> comparer = null) {
comparer ??= Comparer<T>.Default;
if (comparer is null)
throw new ArgumentNullException(nameof(comparer));
if (head is null)
return; // empty linked list
bool agenda = true;
while (agenda) {
agenda = false;
for (Node<T> node = head; !ReferenceEquals(node.Next, head); node = node.Next)
if (comparer.Compare(node.Value, node.Next.Value) > 0) {
agenda = true;
var help = node.Value;
node.Value = node.Next.Value;
node.Next.Value = help;
}
}
}
Edit: If you want to sort some custom type (T) you should either ensure the T implements IComparable<T>:
public class MyClass: IComparable<MyClass> {
...
public int CompareTo(MyClass other) {
TODO: return 0 if other == this, -1 if this < other, +1 if this > other
}
}
or you should implement a comparer IComparer<MyClass> which you should provide as an argument:
public class MyComparer<MyClass> {
public int Compare(MyClass x, MyClass y) {
//TODO: return 0 when x == y, -1 when x < y, when x > y
}
}
then
MyCircularLinkedList.BubbleSort(new MyComparer());
I would like to set a property called "Current" which give me the opportunity to keep track of which item I'm working with. Here's my class Volume:
namespace ConsoleApp3 {
class Volume : Interface
{
public string Name { get; set; }
public Volume()
{
}
}
My interface called: "Interface"
namespace ConsoleApp3{
class Interface
{
public string Name { get; set; }
public int Id { get; set; }
public bool IsCurrent { get; set; }
}
And my RootList Class:
namespace ConsoleApp3{
class RootList<T> : List<T> where T : Interface, new()
{
private int _index;
private int _id;
public RootList()
{
}
public T First
{
get => this[0];
set => this[0] = value;
}
public T Last
{
get => this[this.Count - 1];
set => this[this.Count - 1] = value;
}
public T Current
{
get
{
return this.FirstOrDefault(tTemp => tTemp.IsCurrent == true);
}
set
{
this.RmCurrent();
int _index = this.IndexOf(value);
this[_index].IsCurrent = true;
}
}
public T this[string name]
{
get
{
return this.FirstOrDefault(tTemp => tTemp.Name == name);
}
}
private void RmCurrent()
{
var _iscurrent = this.Where(v => v.IsCurrent = true);
foreach (var item in _iscurrent)
{
item.IsCurrent = false;
}
}
public void Add()
{
this.Add(new T());
this.RmCurrent();
this.Last.IsCurrent = true;
}
}
}
I'm using the "IsCurrent" property to track my items: my item has the "Current" state if the "IsCurrent" property is "true". Only one item can me the "Current" one at the same time.
My "Current" method doesn't work. For exmaple:
VolumeList.Add();
VolumeList.Last.Name = "test_0";
VolumeList.Add();
VolumeList.Last.Name = "test_1";
VolumeList.Add();
VolumeList.Last.Name = "test_2";
In this case, my Current item is the Last one "test_2".
If I do that:
VolumeList.Current = VolumeList[1];
I have two Current items VolumeList[0] and VolumeList[1]. Not only VolumeList[1].
As you can see, I also have a string indexer in my RootList so it has to work with int indexer and string indexer.
Do you have any ideas?
Thanks a lot.
Best regards,
I would not search for an item which is current in private void RmCurrent()
I would only do
private void RmCurrent()
{
foreach (var item in this)
{
item.IsCurrent = false;
}
}
I'd make the RootList track current:
public class RootList<T> : List<T> where T : Interface, new()
{
public T Current { get; private set; }
public T First
{
get => Current = this[0];
set => Current = this[0] = value;
}
public T Last
{
get => Current = this[this.Count - 1];
set => Current = this[this.Count - 1] = value;
}
public T this[string name]
{
//what if there is no such name? what do you want current to be?
//if it should not change, break this lambda up using a temp var
//that you do not assign to current if it is null
get => Current = this.FirstOrDefault(tTemp => tTemp.Name == name);
}
public void Add() //careful, the List<T> you inherit from also has an Add(T), and so calling myList.Add(something) it won't set the current item
{
Current = new T();
this.Add(Current);
}
}
So what's going on ?
Assignments in C# return, as a value, the value that was assigned. It means that this:
myString = "hello";
returns "hello". It can be used in another assignment:
myString2 = (myString = "hello");
Both the strings are "hello";
Single line lambdas must be a value, so this:
get => Current = this[0];
Assigns the Current to be the first item in the list and then gives the first item in the list to the get, to be returned. It's like saying:
get {
Current = this[0];
return Current;
}
Your Current is now the first item in the list, just because you accessed the First property
I'm getting a stack overflow exceptin in the setter of this code for Node from the nested data class
How should the nested data class Desc be handled in the base and derived classes, so that I can use this data in the new nodes created in the main window?
namespace Lib
{
// Nested Data Class
public class Desc
{
public Desc(string shape, Nullable<bool>[] inpins)
{
this.inpins = inpins;
}
string shape { get; set; }
Nullable<bool>[] inpins { get; set; }
}
// Base class drived from ShapeNode class in vendor's framework
public class Node : ShapeNode
{
public Node()
{
}
// Make a copy of Node
public Node(Node copy)
: base(copy)
{
Text = copy.Text;
NodeId = copy.NodeId;
}
public virtual Node Clone()
{
return new Node(this);
}
// Base Constructor
public Node(string Text, Desc NodeId)
{
this.Text = Text;
this.NodeId = NodeId;
}
new public string Text { get { return base.Text; } set { base.Text = value; } }
public Desc NodeId { get { return NodeId; } set { NodeId = value; }
}
}
namespace Test
{
// Main Window code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
nodes = new Node[]
{ new A(
"TESTA",
new Desc(new Nullable<bool>[]{false, false})),
new B(
"TESTB",
new Desc(new Nullable<bool>[] {false, false, false}))
}
}
}
Property getter (and setter) is just a method with special signature and name. So we can rewrite getter of NodeId as:
public Desc get_NodeId()
{
// recursive call
return get_NodeId();
}
To solve this problem, just replace
public Desc NodeId { get { return NodeId; } set { NodeId = value; }}
by
public Desc NodeId { get; set; }
In this case we have (when this method isn't inlined):
public Desc get_NodeId()
{
// compiler-generated backing field
return _nodeId;
}
This is a loop and causes the a stack overflow exception
NodeId { get { return NodeId; }
Use the answer from 2kay (+1) or use a private variable with another name
private Desc nodeID;
public Desc NodeId { get { return nodeID; } set { nodeID= value; }
I am building a treeview with a list of ScanItem. The class of ScanItem is actually:
public class ScanItem
{
public string FullPath { get; set; }
public string Name
{
get
{
return Path.GetFileName(FullPath);
}
}
public DateTime ModifiedDate { get; set; }
public DateTime CreatedDate { get; set; }
public FileAttributes Attributes { get; set; }
public bool IsDirectory { get; set; }
public string Extension
{
get
{
if (IsDirectory)
return "Folder";
else
return Path.GetExtension(Name);
}
}
public UInt64 Size { get; set; }
}
In order for me to create a treeview I needed to create two other classes in order to distinguish the folders and files in my treeview:
public class ScanFile : ScanItem
{
}
public class ScanDir : ScanItem
{
public List<ScanItem> Items { get; set; }
public ScanDir()
{
Items = new List<ScanItem>();
}
}
Note that the class ScanFile is just like the ScanItem and the ScanDir class has an extra property called Items and will contain a list of items of itself.
So if I where to iterate through this direcotory (C:\Temp):
my List will actually contain:
note that if I expand one ScanDir object I will get another List:
in order to populate the following treeview:
So I was able to populate this list using recursion by searching for files and directories in a specific path.
I just wanted to explain my situation because there are several places in the internet that enable you to filter a treeview and that is what I actually want to do. But it will be nice if I can iterate through each item in List and then remove it if some criteria is not met:
I have actually tried using the following recursive method to filter my results.
public List<ScanItem> search(List<ScanItem> items)
{
var filter = new List<ScanItem>();
foreach (var item in items)
{
if (!item.FullPath.Contains("stringIwantToLookFor")) continue;
filter.Add(item);
if (item.IsDirectory)
{
search(((ScanDir)item).Items);
}
}
return filter;
}
I think that if an item is found I need to add all the parent root directories and that's why it does not work. The reason why I want to build my own recursion method is because I want to be able to filter the treeview based on spesific criteria.
EDIT:
In other words if I want to have all the items that contain "X.txt" in my listview I want to just see:
I would do it like this: create public abstract ScanItem Seach(string s) on your ScanItem. You can then call it with the string you want to search for.
The actual implementation would look like this:
ScanFile:
public override ScanItem Seach(string s)
{
if (Name.Contains(s))
return this;
return null;
}
ScanDir:
public override ScanItem Seach(string s)
{
var results = Items.Select(i => i.Seach(s)).Where(i => i != null).ToList();
if (results.Any())
{
var result = (ScanDir)MemberwiseClone();
result.Items = results;
return result;
}
return null;
}
The implementation in ScanFile is easy: if the file matches, return it, else return null. In ScanDir, call Search on all child items recursively. If any of them returned non-null, create a copy of the current object and set the Items of the copy only to those that matched. If none matched, return null.
Note that this will search only through the names of files, not directories. But if you want to do that, such modification is going to be straight-forward.
You should treat the directories a little different because now, if the root directory does not meet the criteria the routine will exit immediately.
Try this: change your ScanItem a little:
public class ScanItem {
...
public virtual bool IsDirectory { get; }
...
}
add this to your scanFile:
public class ScanFile : ScanItem {
public override bool IsDirectory {
get { return false; }
}
}
and this to your scanDir:
public class ScanDir : ScanItem {
public List<ScanItem> Items { get; set; }
public ScanDir() {
Items = new List<ScanItem>();
}
public ScanDir CopyWithoutChildren() {
return new ScanDir() {
FullPath = this.FullPath,
ModifiedDate = this.ModifiedDate,
CreatedDate = this.CreatedDate,
Attributes = this.Attributes,
Size = this.Size
};
}
public override bool IsDirectory {
get { return true; }
}
}
Now do the filtering on the files, omitting empty directories:
public List<ScanItem> search(List<ScanItem> items) {
var filter = new List<ScanItem>();
foreach(var item in items) {
if(item.IsDirectory) {
List<ScanItem> potential = search(((ScanDir)item).Items);
if(potential.Count > 0) {
ScanDir dir = ((ScanDir)item).CopyWithoutChildren();
dir.Items.AddRange(potential);
filter.Add(dir);
}
} else {
if(!item.FullPath.Contains("stringIwantToLookFor")) continue;
filter.Add(item);
}
}
return filter;
}
I didn't test it, but I guess that should do what you want.
I realized my comment to your post might not have been descriptive enough, so I've written some C#-ish pseudocode to demonstrate what I was getting at.
Here's an example of using the Visitor pattern to implement search in a polymorphic, loosely-coupled way:
interface FilesystemVistor
{
void Visit (FilesystemItem item);
}
interface FilesystemItem
{
void Accept(FilesystemVistor visitor);
string Name;
}
class Directory : FilesystemItem
{
private FilesystemItem[] _children;
public void Accept(FilesystemVistor visitor) {
visitor.Visit(this);
foreach(FilesystemItem item in _children)
{
visitor.Visit(item);
}
}
}
class File : FilesystemItem
{
public void Accept(FilesystemVistor visitor) {
visitor.Visit(this);
}
}
class FilesystemSearcher : FilesystemVistor
{
private List<string> _results;
public void Visit(FilesystemItem item) {
if (item.Name == "Foo") { _results.Add(item.Name); }
}
}
This "visitor pattern"-based design will allow you to implement any kind of search without having the search algorithm having to "know" anything about the structure of the file system and the file system doesn't need an extra property like "IsDirectory" to expose its implementation details.
so If I am looking for the files that contain foo this method will populate the files that contain foo in the list 'newList' . I would have to set that list equal to a new list before calling that method. I am obviously missing basic implementation such as changing foo for a parameter etc. I am also missing to remove the empty directories I am working on that.
private List<ScanDir> history = new List<ScanDir>();
private ScanDir LastDir;
private List<ScanItem> newList = new List<ScanItem>();
public void Search(List<ScanItem> allItems) //adds files that contain foo
{
bool updateLastDir = false;
foreach(ScanItem s in allItems)
{
if (updateLastDir)
{
history = (from a in history
select a).Distinct().ToList();
LastDir = null;
for (int i = history.Count - 1; i >= 0; i--)
{
if (history[i].FullPath == Directory.GetParent(s.FullPath).ToString())
{
LastDir = history[i];
break;
}
}
updateLastDir = false;
}
if (s.IsDirectory)
{
var temp = new ScanDir { FullPath = s.FullPath, IsDirectory = true, comparePath = s.comparePath, Attributes = s.Attributes };
if (LastDir == null)
{
newList.Add(temp);
}
else
{
LastDir.Items.Add(temp);
}
LastDir = temp;
history.Add(LastDir);
Search(((ScanDir)s).Items);
history.RemoveAt(history.Count - 1);
updateLastDir = true;
}
else
{
if (s.Name.Contains("Foo")) // then add it
{
if (LastDir == null)
newList.Add(s);
else
LastDir.Items.Add(s);
}
}
}
}
I have seen quite a few articles on here about my question but none really answer what I am asking. I am creating a class of my Branch objects that you can envision as just like the TreeNode objects of the TreeView control. Each Branch can have any number of Branch children below (and therefore above) it. Here is my rather simple class:
public class Branch {
public string Name { get; set; }
public string Link { get; set; }
public Branch Parent { get; private set; }
public List<Branch> Children { get; set; }
internal Branch(string Name, string Link) {
this.Name = Name;
this.Link = Link;
this.Children = new List<Branch>();
} // Branch - Constructor - Overload
internal Branch(string Name, string Link, List<Branch> Children) {
this.Name = Name;
this.Link = Link;
this.Children = Children;
this.Children.ForEach(delegate(Branch branch) {
branch.Parent = this;
});
} // Branch - Constructor - Overload
public bool HasChildren {
get { return this.Children.Count > 0; }
} // HasChildren - Property - ReadOnly
public string Path {
get {
string Result = "";
Branch parent = this;
while (parent != null) {
Result = string.Format("{0}/{1}", parent.Name, Result);
parent = parent.Parent;
} // while stepping up the tree
return string.IsNullOrWhiteSpace(Result) ? "" : Result.Substring(0, Result.Length - 1);
} // get
} // Path - Property - ReadOnly
This works GREAT if I Add children at the time of instantiation like the following:
List<Branch> Branches = new List<Branch>() {
new Branch("First", "#"),
new Branch("Second", "#"),
new Branch("Third", "#", new List<Branch>() {
new Branch("ThirdSub1", "#"),
new Branch("ThirdSub2", "#")
}),
new Branch("Fourth", "#"),
new Branch("Fifth", "#"),
new Branch("Sixth", "#", new List<Branch>() {
new Branch("SixthSub1", "#"),
new Branch("SixthSub2", "#", new List<Branch>() {
new Branch("SixthSub2Sub1", "#"),
new Branch("SixthSub2Sub2", "#"),
new Branch("SixthSub2Sub3", "#", new List<Branch>() {
new Branch("Deep Deep Deep Undercover", "#"),
}),
}),
}),
new Branch("Seventh", "#"),
new Branch("Eighth", "#"),
};
But if I do the following:
List<Branch> Branches = new List<Branch>();
Branch Test = Branches.Add(new Branch("Something", ""));
Test.Children.Add(new Branch("Child Here", ""));
The "Child Here" node does NOT have a Parent associated with it. Thus it is broken and of course the Path property doesn't work property.
I thought I could override the List's Add method but that is not allowed. What is the best way to handle this? Currently I am not creating my own Collection Class like MyBranches, which I like, but if there is a way of doing what I need while implementing IList or ISet or Collection, then I am willing to do so. But please provide an example.
Thanks!
Just for people in the future looking for this same solution, here is the full class:
public class Branch {
public string Name { get; set; }
public string Link { get; set; }
public Branch Parent { get; set; }
public TreeBranches Children { get; private set; }
internal Branch(string Name, string Link) {
this.Name = Name;
this.Link = Link;
this.Children = new TreeBranches(this);
} // Branch - Constructor - Overload
internal Branch(string Name, string Link, TreeBranches Children) {
this.Name = Name;
this.Link = Link;
this.Children = Children;
this.Children.ToList().ForEach(delegate(Branch branch) {
branch.Parent = this;
});
} // Branch - Constructor - Overload
/// <summary>
/// Returns a boolean indicating if the given Branch has any child Branches.
/// </summary>
public bool HasChildren {
get { return this.Children.Count > 0; }
} // HasChildren - Property - ReadOnly
/// <summary>
/// Gets the path from the oldest ancestor to the current Branch.
/// </summary>
public string Path {
get {
string Result = "";
Branch parent = this;
while (parent != null) {
Result = string.Format("{0}/{1}", parent.Name, Result);
parent = parent.Parent;
} // while stepping up the tree
return string.IsNullOrWhiteSpace(Result) ? "" : Result.Substring(0, Result.Length - 1);
} // get
} // Path - Property - ReadOnly
} // Branch - Class
public class TreeBranches : IList<Branch> {
private List<Branch> branches = new List<Branch>();
private Branch owner;
public TreeBranches() {
this.owner = null;
}
public TreeBranches(Branch owner) {
this.owner = owner;
}
public void Add(Branch branch) {
branch.Parent = this.owner;
this.branches.Add(branch);
}
#region Standard IList Method Implementation
IEnumerator<Branch> IEnumerable<Branch>.GetEnumerator() { return this.branches.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.branches.GetEnumerator(); }
public int IndexOf(Branch item) { return this.branches.IndexOf(item); }
public void Insert(int index, Branch item) { this.branches.Insert(index, item); }
public void RemoveAt(int index) { this.branches.RemoveAt(index); }
public Branch this[int index] {
get { return this.branches[index]; }
set { this.branches[index] = value; }
}
public void Clear() { this.branches.Clear(); }
public bool Contains(Branch item) { return this.branches.Contains(item); }
public void CopyTo(Branch[] array, int arrayIndex) { this.branches.CopyTo(array, arrayIndex); }
public int Count { get { return this.branches.Count(); } }
public bool IsReadOnly { get { return this.IsReadOnly; } }
public bool Remove(Branch item) { return this.branches.Remove(item); }
#endregion Standard IList Method Implementation
} // TreeBranches - Class
You can derive from Collection<T> instead of List<T>, List<T> is faster, and is optimized for performance, but Collection<T> is more extensible and allows you to override Add() and others.
If performance is not an issue, then use Collection<T>, and if performance is an issue than use Reed's example of containing a List<T> in your own class.