Setting properties for each sub element of each sub element - c#

I have a set of classes I populate from a 3rd party XML (so I can't really control the structure that much):
public class Category
{
// some properties...
public Tournament[] Tournaments {get;set;}
}
public class Tournament
{
// some properties...
public Match[] Matches {get;set;}
}
public class Match
{
// some properties...
public Competitors Competitors {get;set;}
}
public class Competitors
{
// some properties...
public Teams[] Teams {get;set;}
}
and so on.
Now, I need on the team level to set some properties from different classes -
for instance, I need the id of the category, the gender of the tournament etc.
So for now, I have a foreach loop that sets the category id for each tournament, and later on in the code I have 3 nested foreach loops to copy values from properties of the tournaments to properties the teams.
foreach (var tournament in tournaments)
{
foreach (var match in tournament.Matches)
{
match.SomeProperty = tournament.SomeProperty;
foreach (var team in match.Competitors.Teams)
{
team.CategoryId = tournament.CategoryId;
team.Gender = tournament.Gender;
// and a few more here...
}
}
}
Needless to say this is quite an ugly code.
I was wondering if there is a better way to do it, perhaps using LINQ or something like that.
Update:
For now I have changed my code so that each class has a reference to it's parent. This way I don't need to set any more properties down the chain.
Instead of having team.CategoryId I'm now using team.Match.Tournament.Category.Id
However, I still have have the same problem, only now it's concentrated in only one place - After the deserialization is completed, I have 4 nested foreach loops - so it looks like this:
foreach (var category in MainClass.Categories)
{
category.FileName = MainClass.FileName;
foreach (var tournament in category.Tournaments)
{
tournament.Category = category;
foreach (var match in tournament.Matches)
{
match.Tournament = tournament;
foreach (var team in match.Fixture.Competitors.Teams)
{
team.Match = match;
}
}
}
}
It would be nice to find a way to avoid these nested loops...

You can merge the both inner foreach with the SelectMany-LINQ operation.
foreach (var tournament in tournaments)
{
//// We take every team in every competitors to on Enumeration
foreach (var team in tournament.Matches.SelectMany(match => match.Competitors.Teams))
{
team.CategoryId = tournament.CategoryId;
team.Gender = tournament.Gender;
}
}
Edit:
When you set an property at the first foreach, the SelectMany will not work (because the projection will only hold all teams from the matchs Competitors).
One way to make it a little bit nicer, would be to extract a method
foreach (var tournament in tournaments)
{
SetMatchPropertiesFromTournament(tournament);
}
...
private void SetMatchPropertiesFromTournament(Tournament tournament)
{
foreach (var match in tournament.Matches)
{
match.SomeProperty = tournament.SomeProperty;
foreach (var team in match.Competitors.Teams)
{
team.CategoryId = tournament.CategoryId;
team.Gender = tournament.Gender;
// and a few more here...
}
}
}
A nice, small function, which just does only one thing... thats a great thing!

Related

C# - LINQ - Sorting collection by double-nested properties

Let's assume that we have a few classes:
public class SecondNestingLevel
{
public string SortPropety {get; set;}
}
public class FirstNestingLevel
{
public ICollection<SecondNestingLevel> SecondNestingLevelCollection {get; set;}
}
public class Wrapper
{
public ICollection<FirstNestingLevel> FirstNestingLevelCollection {get; set;}
}
I have to sort collection of Wrapper objects by SortPropety (located inside double-nested SecondNestingLevel objects).
Guidelines:
Firstly, aggregate together Wrapper objects, which every value of SortPropety is the same (in every FirstNestingLevel and SecondNestingLevel located inside specific Wrapper object).
Secondly, sort aggregated in this way Wrapper objects in a alphabetic way.
General schema of result collection:
Part 1: Wrappers with the same value of SortProperty sorted in an alphabetic way.
Part 2: Other wrappers sorted in an alphabetic way of SortProperty located in first SecondNestingLevel object which is placed in first FirstNestingLevel object in nested collections.
I would be very grateful for your help.
To order SortProperty I made this example:
var wrapper = new Wrapper()
{
FirstNestingLevelCollection = new List<FirstNestingLevel>()
{
new FirstNestingLevel() { SecondNestingLevelCollection = new List<SecondNestingLevel>()
{
new SecondNestingLevel() { SortPropety = "11" },
new SecondNestingLevel() { SortPropety = "02" },
new SecondNestingLevel() { SortPropety = "05" }
}
}
}
};
Now to order SortPropety you need to go through each element in firstNestingLevel to be able to order SecondNestingLevel.
foreach (var firstNestingLevel in wrapper.FirstNestingLevelCollection)
{
var orderedEnumerable = firstNestingLevel
.SecondNestingLevelCollection.OrderBy(e => e.SortPropety);
foreach (var secondNestingLevel in orderedEnumerable)
{
Console.WriteLine(secondNestingLevel.SortPropety);
}
}
If you need it in Linq, one way to go is using Select and you will get a nested loop of ordered elements:
var orderedEnumerable = wrapper.FirstNestingLevelCollection
.Select(e => e.SecondNestingLevelCollection.OrderBy(e1 => e1.SortPropety));
To print that you can do something like:
foreach (var secondNestingLevels in orderedEnumerable)
{
foreach (var secondNestingLevel in secondNestingLevels)
{
Console.WriteLine(secondNestingLevel.SortPropety);
}
}
Both codes will give the same output:
02
05
11

C# How to access a brother class from within a class function?

I have a code structured like the one below. I simplified it with just the strictly needed to pose the question.
I have a Parent class, which includes an Item1 class, Item2 class, Item1_to_Item2_relationship class. I must keep this structure for reasons not relevant to this problem.
How can I access a value in Item1 from Item2?
The code explains better what needs to be done.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
Parent parent = new Parent();
// Adding item1 values
Item1 item1_01 = new Item1();
item1_01.id = "item1_01";
item1_01.item1_code = "CODE_01";
parent.items1.Add(item1_01);
Item1 item1_02 = new Item1();
item1_02.id = "item1_02";
item1_02.item1_code = "CODE_02";
parent.items1.Add(item1_02);
// Adding item2 values
Item2 item2 = new Item2();
item2.id = "item2_01";
parent.items2.Add(item2);
// Adding relationships
Item1_to_Item2_Relationship item1_to_Item2_Relationship = new Item1_to_Item2_Relationship();
item1_to_Item2_Relationship.item1.id_alt = item1_01.id;
item1_to_Item2_Relationship.item2.id_alt = item2.id;
parent.Item1_to_Item2_Relationships.Add(item1_to_Item2_Relationship);
item1_to_Item2_Relationship = new Item1_to_Item2_Relationship();
item1_to_Item2_Relationship.item1.id_alt = item1_02.id;
item1_to_Item2_Relationship.item2.id_alt = item2.id;
parent.Item1_to_Item2_Relationships.Add(item1_to_Item2_Relationship);
// How to make the code below return a List<string> with the values "CODE_01" and "CODE_02"?
foreach (Item2 my_item2 in parent.items2)
{
my_item2.item1_codes;
}
}
}
class Parent
{
public List<Item1> items1;
public List<Item2> items2;
public List<Item1_to_Item2_Relationship> Item1_to_Item2_Relationships;
public Parent()
{
items1 = new List<Item1>();
items2 = new List<Item2>();
Item1_to_Item2_Relationships = new List<Item1_to_Item2_Relationship>();
}
}
class Item1
{
public string id;
public string id_alt;
public string item1_code;
public Item1()
{
id = "";
item1_code = "";
}
}
class Item2
{
public string id;
public string id_alt;
public Item2()
{
id = "";
}
}
class Item1_to_Item2_Relationship
{
public Item1 item1;
public Item2 item2;
public Item1_to_Item2_Relationship()
{
item1 = new Item1();
item2 = new Item2();
}
}
}
At the moment, I would have to right a static function which receives a Parent parameter and do the logic there. But I believe there should be a better more intuitive way. How can I make the above code work?
As mentioned in other answers you can access it directly. Since you are maintaining a relationship, I assume you want to do it via the relationship
var test = new List<String>();
foreach (Item2 my_item2 in parent.items2)
{
foreach (var item in parent.Item1_to_Item2_Relationships)
{
//implement your own equality comparision if it should be differernt
if (item.item2.id_alt == my_item2.id)
{
test.Add(item.item1.item1_code);
}
}
}
Few important points:
You are dealing with list and nested loops, the performance of the
approach is not best.
To improve the performance the relationship data structure should be
chosen wisely, is it possible to convert it to Dictionary, thus
giving quicker lookup times.
The code currently will add blank string since you do not add the Code in the relationship, but you can always search for object using the id, that means another search in list, this is the reason I am saying that you might want to change the underlying data structure.
It might be better to store the individual objects also in a dictionary if their item code is the primary key.
The fastest and easiest way to do this would be to pass in parent into the function in which itemX would need to access a property from itemY.
For instance:
class Item1
{
public string id;
public string id_alt;
public string item1_code;
public Item1()
{
id = "";
item1_code = "";
}
}
class Item2
{
public string id;
public string id_alt;
public Item2()
{
id = "";
}
public void AccessItem1Prop(Parent parent) {
string item1ID = parent.items1[0].id;
}
}
You could also pass in parent to the individual items constructor and attach it to the item as an object property, but I'd avoid that approach if you can simply to keep things cleaner.
I'm not a fan of how the code is structured... but here's the simple answer.
You're adding two different item types to two different collections.
You're making a relationship item, that relates the two based on id.
Although your code screams help the idea is logical and a good way to relate items such as working with a database.
We can do this simply with Linq but I would rather stick closer to how you're writing the question.
First iterate your items like you're doing, then iterate your relationships. Based on Id comparison you then iterate the other items and based on that final Id comparison you get your answer.
foreach (Item2 my_item2 in parent.items2)
{
foreach (Item1_to_Item2_Relationship relatedItem in parent.Item1_to_Item2_Relationships)
{
if (my_item2.id == relatedItem.item2.id_alt)
{
foreach (Item1 item1 in parent.items1)
{
if (relatedItem.item1.id_alt == item1.id)
{
Console.WriteLine(item1.item1_code);
}
}
}
}
}
//Outputs
//CODE_01
//CODE_02
'just do it'
var i1 = new Item1();
var i2 = new Item2();
i1.id = i2.id;
Now others will argue that you should use properties rather than public fields. But what you have at the moment allows you to do exactly what you want.

Is it possible to get value of a method thru Property

I have to export data to Excel programmatically. I have a class with several properties. I was wondering if it's possible to retrieve values of all properties using a loop. For instance:
public class SqueezeProperties
{
public int WidthField { get; set; }
public string Width_unit { get; set; }
public int ResPressure { get; set; }
public int DensityField { get; set; }
......
}
While writing to excel, I code as:
t = typeof(SqueezeProperties);
foreach (PropertyInfo field in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
oSheet.Cells[r, c++] = field.Name;
Now to input values, is there any way that I can iterate and find values of all properties that can be accessed, and store them in excel?
I doubt if it is even possible. I just taught myself how to access the property name & its details, so I thought maybe the other thing could also be possible and I am simply unaware of it.
Thanks
You can use PropertyInfo.GetValue.
(However according to specification the order of your properties is not guaranteed to be the same as the definition order. So to be safe you might want to order them.)
Also instead of getting them via reflection you could create a collection manually instead, this would take care of the order already.
e.g.
var properties = new Expression<Func<SqueezeProperties, object>>[]
{
o => o.WidthField,
o => o.Width_unit,
//...
};
foreach (var exp in properties)
{
var mem = (MemberExpression)exp.Body;
var prop = (PropertyInfo)mem.Member;
oSheet.Cells[r, c++] = prop.GetValue(squeezePropertiesInstance, null);
}

Building a tree using a list of objects

I have a list of objects with property id and parent_id.
I want to build a tree to link up those children and parents.
1 parent may have several children and there is an object which will be the ancestor of all objects.
What's the fastest algorithm to implement that?
I use C# as programming language, but other languages are also okay.
Something like that should do the trick :
public List<Node> MakeTreeFromFlatList(IEnumerable<Node> flatList)
{
var dic = flatList.ToDictionary(n => n.Id, n => n);
var rootNodes = new List<Node>();
foreach(var node in flatList)
{
if (node.ParentId.HasValue)
{
Node parent = dic[node.ParentId.Value];
node.Parent = parent;
parent.Children.Add(node);
}
else
{
rootNodes.Add(node);
}
}
return rootNodes;
}
(assuming that ParentId is a Nullable<int>, and is null for root nodes)
You could use a dictionary:
var dict = new Dictionary<Id, Node>();
foreach (var item in items)
{
dict[item.Id] = new Node(item);
}
foreach (var item in items)
{
dict[item.ParentId].AddChild(dict[item.Id]);
}
I much prefer this kind of structure. By maintaining a single list (you may want to use a dictionary or similar for speed) of items and passing it into the GetChildItems function you have greater flexibilty and ease of sorting, adding, removing, saving to a db etc.
You only really need the GetChildItem function when you are rendering the list to a view and you want the tree structure for easy editing as you say. In this case you can have a view model with the full list and the item which is passed into each item view
public class Item
{
public string Id { get; set; }
public string ParentId { get; set; }
public IEnumerable<Item> GetChildItems(List<Item> allItems)
{
return allItems.Where(i => i.Id == this.ParentId);
}
}
public class Tree
{
public List<Item> Items { get; set; }
public IEnumerable<Item> RootItems(List<Item> allItems)
{
return allItems.Where(i => i.ParentId == null);
}
}
Note: the class structure above is designed to mimic the traditional complex object pattern. these days you would prob just have GetChildItems(List allItems, Item parentItem) in the view model

How to merge 2 or more Lists implementing same interface

Let say I have 2 lists
public List<TypeA> TypeARecords {get; set;}
public List<TypeB> TypeBRecords {get; set;}
Both TypeA and TypeB implements same Interface (let say IBaseRecord)
Now I have a read only property that returns list of all records
public List<IBaseRecord> AllRecords
{
get
{
var allRecs = new List<IBaseRecord>();
foreach ( var rec in TypeARecords)
allRecs.Add(rec);
foreach ( var rec in TypeBRecords)
allRecs.Add(rec);
return allRecs;
}
}
This works but I am sure there is more effective or just smarter way to do same thing
Any ideas?
You can make an iterator that returns the items in the list:
public IEnumerable<IBaseRecord> GetAllRecords {
foreach (var rec in TypeARecords) {
yield return rec;
}
foreach (var rec in TypeBRecords) {
yield return rec;
}
}
This way you don't have to create a new list with all the items, it will just read from the existing lists.
Edit:
As Stan R. suggested, you can use the ToList method to create a copy of the list:
List<IBaseRecord> work = obj.GetAllRecords().ToList();
This is a bit better than having a property that returns a new list, as the ownership of the list gets clearer. Also, a property should not do such heavy lifting as creating lists, at least not every time the property is read.
Your way.
public List<IBaseRecord> AllRecords
{
get
{
return new List<IBaseRecord>().
Concat(TypeARecords.OfType<IBaseRecord>()).
Concat(TypeBRecords.OfType<IBaseRecord>()).
ToList();
}
}
Better way.
public IEnumerable<IBaseRecord> AllRecords
{
get
{
foreach (var i in TypeARecords) yield return i;
foreach (var i in TypeBRecords) yield return i;
}
}
Best way IMHO.
public IEnumerable<IBaseRecord> AllRecords
{
get
{
return TypeARecords.Concat<IBaseRecord>(TypeBRecords);
}
}
Unless you use the common interface to declare your 2 lists, you can't do this until C# 4 without something similar to what you mentioned (or the linq equivalent).
You can use List.AddRange(), but that's still an O(n) operation, implying that it iterates over all of the members being added, so it's just essentially syntatic sugar for what you're already doing.
Presumably you don't want to modify either ListA or ListB, so you will have to iterate in order to create the new pointers for your new list.
What Blindy means is unless you do
public List<IBaseRecord> TypeARecords {get; set;}
public List<IBaseRecord> TypeBRecords {get; set;}
Then you can do something like
public IEnumerable<IBaseRecord> AllRecords
{
get
{
return Enumerable.Concat(TypeARecords, TypeBRecords);
}
}
public List<IBaseRecord> AllRecords
{
get
{
return TypeARecords.Cast<IBaseRecord>()
.Concat(TypeBRecords.Cast<IBaseRecord>()).ToList();
}
}

Categories