What kind of data structure to use? - c#

I am working on a project where I need to keep track of:
5-6 Root items of just a string name
Each root item need to have multiple children of different identifier types (int, string, float, etc). All the children of one root will be the same type but each root will have different children types
user will need to be able to add/delete children from each root
i will later need to access each children individually and perform string manipulations and parsing when needed
I've thought about maybe using a dictionary where the Key is a string and Values are lists of objects. Or having a unique class for each root item and each class will include a List of children.
Does anyone have any good suggestions? I'm still quite new to OOP, please bear with me :)
Thanks!

public interface IRoot {}
public class RootItem<T> : IRoot
{
public string Name { get; set; }
public List<T> Children {get; set; }
}
And then keep a Dictionary<string, IRoot> to hold them all.
Dictionary<string, IRoot> hair = new Dictionary<string, IRoot>();
hair.Add(
new RootItem<int>()
{
Name = "None",
Children = new List<int>() {1, 2, 3, 4}
}
);
hair.Add(
new RootItem<decimal>()
{
Name = "None",
Children = new List<decimal>() {1m, 2m, 3m, 4m}
}
);

How about a generic class with a List<T> to contain the children:
public class Root<T>
{
private List<T> children = null;
public Root(string name)
{
Name = name;
}
public string Name { get; set; }
public List<T> Children
{
get
{
if (children == null)
{
children = new List<T>();
}
return children;
}
}
}
Root<int> intRoot = new Root<int>("IntRoot");
intRoot.Children.Add(23);
intRoot.Children.Add(42);
Root<string> stringRoot = new Root<string>("StringRoot");
stringRoot.Children.Add("String1");
stringRoot.Children.Add("String2");
stringRoot.Children.Add("String3");
stringRoot.Children.Add("String4");
If you want to hold all the roots in one object, you could write your own class or use a Tuple:
var rootGroup = Tuple.Create(intRoot, stringRoot);
// intRoot is accessible as rootGroup.Item1
// stringRoot is accessible as rootGroup.Item2

Sounds like Dictionary<string, Tuple<type1, type 2, etc>> is a good candidate.
The key will be the string(root). The children to the root is a Tuple. We can add items to tuple. Thanks for pointing thisout.
Good starting point on Tuple

Here's one way to go about it. There's a lot of casting that needs to happen, but it gets the job done:
static void Main(string[] args)
{
Dictionary<string, IRootCollection> values = new Dictionary<string, IRootCollection>();
values["strings"] = new RootCollection<string>();
(values["strings"] as RootCollection<string>).Add("foo");
(values["strings"] as RootCollection<string>).Add("bar");
values["ints"] = new RootCollection<int>();
(values["ints"] as RootCollection<int>).Add(45);
(values["ints"] as RootCollection<int>).Add(86);
}
interface IRootCollection { }
class RootCollection<T> : List<T>, IRootCollection { }

Related

Trying to use reflection to concatenate lists of objects

I have below class
public class HydronicEquipment
{
public List<LibraryHydronicEquipment> Source { get; set; }
public List<LibraryHydronicEquipment> Distribution { get; set; }
public List<LibraryHydronicEquipment> Terminals { get; set; }
}
and then i have the below class for "libraryHydronicEquipment"
public class LibraryHydronicEquipment : IEquipmentRedundancy
{
public string Name { get; set; }
public RedundancyStatus RedundancyStatus { get; set; }
public EquipmentRedundancy EquipmentRedundancy { get; set; }
}
I am trying to concatenate the list of "LibraryHydronicEquipment" objects available from all three properties (i.e) from source, distribution and terminal and General concatenate method will looks like as this below
var source = hydronicEquipment.Source;
var distribution = hydronicEquipment.Distribution;
var teriminals = hydronicEquipment.Terminals;
Source.Concat(Distribution).Concat(Terminals)
I am trying to achieve the same using reflection and the code looks like as below
foreach (var (systemName, hydronicEquipment) in hydronicSystemEquipment)
{
bool isFirstSystem = true;
var equipmentList = new List<string> { "Source", "Distribution", "Terminals" };
var redundancyequipmentList = GetRedundancyEquipment(hydronicEquipment, equipmentList);
}
and the method GetRedundancyEquipment is looks like below
private static IEnumerable<IEquipmentRedundancy> GetRedundancyEquipment(HydronicEquipment hydronicEquipment, List<string> equipmentList)
{
IEnumerable<IEquipmentRedundancy> equipmentRedundancies = new List<IEquipmentRedundancy>();
dynamic equipmentResults = null;
foreach(var equipment in equipmentList)
{
var componentList = hydronicEquipment.GetType().GetProperty(equipment).GetValue(hydronicEquipment, null) as IEnumerable<IEquipmentRedundancy>;
equipmentResults = equipmentRedundancies.Concat(componentList);
}
return equipmentResults;
}
The problem here is even though i have Source is having list of objects and Distribution is having list of objects, the equipmentResults is giving only one object instead of list of concatenated objects.
I am trying to return the IEnumerable<IEquipmentRedundancy> at the end using reflection method but it seems not working with the above code.
Could any one please let me know how can i achieve this, Many thanks in advance.
GetRedundancyEquipment should preserve your values instead of reassign the reference with each iteration. Here's the fixed version:
private static IEnumerable<IEquipmentRedundancy> GetRedundancyEquipment(HydronicEquipment hydronicEquipment, List<string> equipmentList)
{
IEnumerable<IEquipmentRedundancy> equipmentRedundancies = new List<IEquipmentRedundancy>();
var equipmentResults = new List<IEquipmentRedundancy>();
foreach (var equipment in equipmentList)
{
var componentList = hydronicEquipment.GetType().GetProperty(equipment).GetValue(hydronicEquipment, null) as IEnumerable<IEquipmentRedundancy>;
equipmentResults.AddRange(equipmentRedundancies.Concat(componentList));
}
return equipmentResults;
}
If we look at what you're doing in GetRedundancyEquipment() it becomes clear.
First you create equipmentRedundancies = new List<IEquipmentRedundancy>();
Then you never modify equipmentRedundancies - e.g. via Add(). It remains an empty list until it goes out of scope and is garbage collected.
In a loop you then repeatedly make this assignment equipmentResults = equipmentRedundancies.Concat(componentList);
That is to say: Assign to equipmentResults the concatenation of componentList to equipmentRedundancies.
Note that Concat() is a lazily evaluated linq method. When you actually enumerate it results are produced. It doesn't modify anything, it's more like a description of how to produce a sequence.
So each time through the loop you're assigning a new IEnumerable that describes a concatentaion of an empty list followed by the property that you retrieved with reflection to equipmentResults. Then at the end you return the final one of these concatenations of an empty list and retrieved property.
If you want all of them together, you should concatenate each of them to the result of the previous concatenation, not to an empty list.

How to add data items to a List<Struct>

I have the following struct defined in a user control:
public struct ColumnData
{
public string ColumnName { get; set; }
public string ColumnDataItem { get; set; }
public bool ColumnIsHyperLink { get; set; }
public string ColumnHyperLinkURL { get; set; }
public string ColumnHyperLinkPK { get; set; }
}
I create a new instance of List<ColumnData> (In a different code behind that creates an instance of the user control) and want to pass in values to it, but how do I assign them to specific attributes within the struct object?
I create an instance of the struct using the following code:
List<ColumnData> DataItems = new List<ColumnData>();
This:
List<ColumnData> DataItems = new List<ColumnData>();
creates a new list.
This:
List<ColumnData> DataItems = new List<ColumnData>();
var cd = new ColumnData();
cd.ColumnName = "Taco";
DataItems.Add(cd);
creates a new list, a new struct, and adds an item to the list.
Change that to a class; all your woes relating to modifying struct properties (etc) will go away.
Alternatively, make it an immutable struct, and initialize it with the correct values at the point of creation - then the issue is moot, no matter how many times it is subsequently copied.
IMO the first is the right approach here.
so you need to do this:
public void AddToList(ColumnData columnData)
{
DataItems.Add(columnData);
}
and call this method from your other class passing an already created and initialized columnData object.
List<ColumnData> DataItems = new List<ColumnData>();
Is creating the list that holds your structs not your structs itself.
You could use structs here for performance reasons (faster then classes).
ColumnData data = new ColumnData
{
ColumnName = "Blaa"
};
DataItems.Add(data);

How to create a collection of classes that can be iterated over?

I have a series of properties for an object which are themselves a class:
private ClassThing Thing1;
private ClassThing Thing2;
private ClassThing Thing3;
private class ClassThing
{
public string Name;
public int Foos;
}
In some areas I need to be able to access each property specifically, for example:
label1.Text = Thing1.Name;
However, it is also desirable to create a foreach loop to access each one, like this:
string CombinedString;
foreach(ClassThing Thing in SomeCollection)
{
CombinedString += Thing.Name;
}
The end result must be XML serializable. These examples are very basic, but I hope they more easily demonstrate my need.
I tried creating a dictionary of these properties instead, but a dictionary is not XML serializable. I'd like to simply make all of these properties members of a class that itself can be iterated over, but I'm not sure how.
Can anyone point me in the right direction?
I hope this clarifies some things for you, since i am not entirely sure i understand your question.
//many normal classes can be made xml serializable by adding [Serializable] at the top of the class
[Serializable]
private class ClassThing
{
public string Name { get; set; }
public int Foos { get; set; }
}
//here we create the objects so you can access them later individually
ClassThing thing1 = new ClassThing { Name = "name1", Foos = 1 };
ClassThing thing2 = new ClassThing { Name = "name2", Foos = 2 };
ClassThing thing3 = new ClassThing { Name = "name3", Foos = 3 };
//this is an example of putting them in a list so you can iterate through them later.
List<ClassThing> listOfThings = new List<ClassThing>();
listOfThings.Add(thing1);
listOfThings.Add(thing2);
listOfThings.Add(thing3);
//iteration example
string combined = string.Empty;
foreach (ClassThing thing in listOfThings)
{
combined += thing.Name;
}
//you could also have created them directly in the list, if you didnt need to have a reference for them individually, like this:
listOfThings.Add(new ClassThing { Name = "name4", Foos = 4 });
//and more advanced concepts like linq can also help you aggregate your list to make the combined string. the foreach makes the code more readable though. this gives the same result as the foreach above, ignore it if it confuses you :)
string combined = listOfThings.Aggregate(string.Empty, (current, thing) => current + thing.Name);
//Here is an example of how you could serialize the list of ClassThing objects into a file:
using (FileStream fileStream = new FileStream("classthings.xml", FileMode.Create))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<ClassThing>));
xmlSerializer.Serialize(fileStream, listOfThings);
}
To be able to serialize the objects using this method, they cannot contain a constructor, which is why we use the new ClassThing{Name="",Foos=0} way of creating them.
You're looking for an implementation of the IEnumerable interface. See this link for a quick description of how to implement it.
class MyClass
{
private ClassThing Thing1;
private ClassThing Thing2;
private ClassThing Thing3;
internal IEnumerable<ClassThing> GetThings()
{
yield return Thing1;
yield return Thing2;
yield return Thing3;
}
void Test()
{
foreach(var thing in this.GetThings())
{
//use thing
}
}
}
public List<ClassThing> Things = new List<ClassThing>();
Then you can run your foreach over .Things

How to treat a dictionary of subclasses as a dictionary of the base class

In C# I have a bunch of objects all inheriting from the same base class.
I also have a number of Dictionaries, one for each subclass.
What I want to do is add all of those Dictionaries to one List so I can loop through them all and do some work (like comparing the lists etc).
In summary
Dictionary<string, Child> childObjects = new Dictionary<string, Child>();
List<Dictionary<string, Parent>> listOfDictionaries = new List<Dictionary<string, Parent>>();
listOfDictionaries.Add(childObjects);
I would have thought that since Child inherits from Parent, this should work, but it won't compile. Clearly I am not understanding something about inheritance and generics :)
A full code example
class Program
{
static void Main(string[] args)
{
//Creating a Dictionary with a child object in it
Dictionary<string, Child> childObjects = new Dictionary<string, Child>();
var child = new Child();
childObjects.Add(child.id, child);
//Creating a "parent" Dictionary with a parent and a child object in it
Dictionary<string, Parent> parentObjects = new Dictionary<string, Parent>();
parentObjects.Add(child.id, child);
var parent = new Parent();
parentObjects.Add(parent.id, parent);
//Adding both dictionaries to a general list
List<Dictionary<string, Parent>> listOfDictionaries = new List<Dictionary<string, Parent>>();
listOfDictionaries.Add(childObjects); //This line won't compile
listOfDictionaries.Add(parentObjects);
}
}
class Parent
{
public string id { get; set; }
public Parent()
{
this.id = "1";
}
}
class Child : Parent
{
public Child()
{
this.id = "2";
}
}
Is there any way of achieving this?
You can't do this safely. Imagine you did this:
listOfDictionaries[0]["foo"] = new Parent();
That looks fine - but it would mean that childObjects would contain a value which isn't an instance of Child!
C# 4 has introduced restricted generic variance where it's safe - so you can convert a reference of type IEnumerable<Banana> to IEnumerable<Fruit> for example - but what you're wanting to do here isn't safe, so it still isn't allowed.
If you could tell us more about the bigger context - what you're trying to achieve - we may be able to help more. Can you give examples of what you'd want to do with the list afterwards?

How to remove multiple objects in List by id?

I have a class as below
class MyClass
{
public string id { get; set; }
public string data { get; set; }
}
I have an array of ids I need to remove:
List<myClass> myObjArray = new List<myClass>;
myClass myObj1 = new myClass { id = "1", data = "aaa"};
myClass myObj2 = new myClass { id = "2", data = "bbb"};
myClass myObj3 = new myClass { id = "3", data = "ccc"};
myClass myObj4 = new myClass { id = "4", data = "ddd"};
myObjArray.Add(myObj1);
myObjArray.Add(myObj2);
myObjArray.Add(myObj3);
myObjArray.Add(myObj4);
string [] idToBeRemove = {"1", "3"};
Is there any method to remove the myObj in myObjArray where the id is in the idToBeRemove string?
List<T> has a method RemoveAll which will accomplish what you need. Unlike doing something like Where(...).ToList(), it will modify the existing list in place rather than create a new one.
myObjArray.RemoveAll(item => idToBeRemove.Contains(item.id));
Note that as your array of items to remove grows, you'll want to move to something more performant, such as a HashSet<T>, though with your current count of 2, it is not a pressing need.
#Richardissimo also offers a good suggestion in the comments to your question for when the main list itself grows, a Dictionary<K, V> could be useful. Again, not an immediate need with a small list, but something to keep in mind as it grows and if performance is an issue.
To remove from existing List you can use List.RemoveAll(Predicate):
myObjArray.RemoveAll(r => idToBeRemove.Contains(r.id));
To get result in new collection you can use Enumerable.Where and Enumerable.Contains:
var result = myObjArray.Where(m => !idToBeRemove.Contains(m.id)).ToList();
var result = myObjArray.Where(m => !idToBeRemove.Contains(m.id)).ToList();
foreach (var item in result)
{
myObjArray.Remove(item);
}
Use this to remove id:
myObjArray.RemoveAll(item => idToBeRemove.Contains(item.id));

Categories