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

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

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.

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.

How to compare two lists and change one property

I have a class:
public class Doc
{
public int Id {get; set;}
public string Name {get; set;}
public bool IsActive {get; set;}
}
And two lists of Doc type.
How to write LINQ to compare these list and change IsActive property of first list if contains id from second list.
Given that your target list is named targetDocs and the list you want to check for document existance is srcDocs try something like (don't have access to a compiler here so can't test):
targetDocs.ForEach(d => d.IsActive = srcDocs.Any(sd => sd.id == d.Id))
I'm assuming that we are talking about Lists and not other collection types as the ForEach extension method is defined for Lists.
It's better to use the HashSet<T> collection for such operations, it has fast O(1) lookups and there is no real reason to use LINQ for changing property values here. True, we have to create another collection here and it takes time to allocate resources initialize it etc. but if there is one million records it will give huge performance boost.
Provided that docs1 is collection where you would like to change IsActive to true if docs2 collection has Id, you can use :
var ids = new HashSet<int>(docs2.Select(d => d.Id));
foreach(var doc in docs1)
{
// .Contains implementation in HashSet has fast O(1) lookups
doc.IsActive = ids.Contains(doc.Id);
}
You can create your own static method which is responsible for checking active property. I've used enumerators here, it works fast and easy to understand.
static class Program
{
static void Main(string[] args)
{
List<Data> firstInstance = new List<Data>
{
new Data { Id = 1, IsActive = false },
new Data { Id = 2, IsActive = false }
};
List<Data> secondInstance = new List<Data>
{
new Data { Id = 1, IsActive = false },
new Data { Id = 3, IsActive = false }
};
firstInstance.CheckActive(secondInstance);
}
static void CheckActive(this List<Data> firstInstance, List<Data> secondInstance)
{
using (IEnumerator<Data> firstEnumerator = firstInstance.GetEnumerator(), secondEnumerator = secondInstance.GetEnumerator())
{
while (true)
{
if (!firstEnumerator.MoveNext() || !secondEnumerator.MoveNext()) break;
if (firstEnumerator.Current.Id == secondEnumerator.Current.Id) firstEnumerator.Current.IsActive = true;
}
}
}
}
class Data
{
public int Id { get; set; }
public bool IsActive { get; set; }
}
You wrote:
How to write linq to compare these list and change "IsActive" propety of first list if contains id from second list.
Your first list does not have an IsActive property, however the elements of your list have one. Therefore I assume you want the following:
How do I get a sequence of all elements in the first list that have an Id of any of the elements in the second list, so I can change the IsActive properties of those elements.
LINQ is used to enumerate over objects. You can't change the object in the linq statement
If you want to change the objects, you'll have to enumerate over them, for instance with ForEach.
IEnumerable<int> activeIds = secondList.Select(item => item.Id);
IEnumerable<Doc> activeDocs = firstList.Where(item => activedIds.Contains(item.Id));
foreach (Doc activeDoc in activeDocs)
{
activeDoc.IsActive = true;
}
Beware: I did not change ISActive for all inActive docs. If you want that you'll have to foreach all elements from your firstList:
IEnumerable<int> activeIds = secondList.Select(item => item.Id);
foreach (Doc doc in firstList)
{
doc.IsActive = activeIds.Contains(doc.Id);
}
You can use this solution without using linq
foreach(Doc x in list1){
foreach(Doc y in list2){
if(x.id == y.id){
y.IsActive = true;
}
}
}

Sort list based on name of object in list?

Apologies if this is a duplicate question, which I think it may be although I cannot find the answer I am looking for.
In my list, I need to sort by the names of the objects in the list. As an example, if in a list of cars I have the objects Volvo, Ford, BMW, Audi, Ferrari and Lamborghini in that order, then I would need it to order the objects Audi then BMW etc.
I have tried doing .OrderBy(x => nameof(x)) but this didn't work. Does anyone have any solutions
Edit: I need to order by the following (other elements hidden because of sensitive data):
So on Data there is an object called "Aaa" for example which needs to be the first element in this list.
Instead of relying on the variables names within a collection you should use a Name-property for every instances within that list:
class Car
{
public string Name { get; set; }
// ...
}
Now within your consuming code you can easily use this property:
var myList = new List<Car> {
new Car { Name = "BMW" },
new Car { Name = "Audi" }
}:
var result = myList.OrderBy(x => x.Name);
As Abion47 and me already mentioned the names of variables have no meaning to the assembly, just within your sour-code. When you de-compile your assembly you see that the names are replaced by a random dummy-name. So relying on those names is hardly a good idea.
Below is one of the way using IComparer interface.
//Implementing IComparer
public class Cars
{
string Name { get; set; }
}
public class SortByName : IComparer<Cars>
{
public int Compare(Cars first, Cars next)
{
return first.Name.CompareTo(next.Name);
}
}
//Main Method
static void Main(string[] args)
{
Cars one = new Cars { Name = "Ford" };
Cars two = new Cars { Name = "Audi" };
Cars three = new Cars { Name = "Lamborghini" };
Cars four = new Cars { Name = "Ferrari" };
List<Cars> _lstCars = new List<Cars>();
_lstCars.Add(one);
_lstCars.Add(two);
_lstCars.Add(three);
_lstCars.Add(four);
SortByName sortbyname = new SortByName();
_lstCars.Sort(sortbyname);
Console.WriteLine("Result After sorting by name");
foreach (var item in _lstCars)
{
Console.WriteLine(item.Name);
Console.WriteLine();
}
Console.Read();
}

Using LINQ to loop through inner class properties in outer class collection

Leveraging off the Q&As dealing with looping through an object's properties (https://stackoverflow.com/questions/15586123/loop-through-object-and-get-properties, Loop Through An Objects Properties In C#), where you have:
a collection of Class1 objects (i.e. listObj1)
each Class1 contains a collection of Class2 objects (i.e. dictObj2)
How would you:
efficiently determine the properties of the inner class (Class2)
loop through the the properties of the inner class (Class2)
loop through the collection of Class1 objects (listObj1) selecting all instances of the the Class2 property
output the collection of Class2 property (e.g. the first iteration would return a collection of MeasurementA, one from each Class1 object).
and group the collection by Class1.PropertyA and Class1.PropertyB
Please find below a rough map of the classes involved.
I have been trying to use a LINQ query without success. Any ideas or guidance would be greatly appreciated.
class MainClass {
List<Class1> listObj1
}
class Class1 {
// a number of fields including...
int PropertyA { get; set; }
int PropertyB { get; set; }
Dictionary<int, Class2> dictObj2 { get; set; }
}
class Class2 {
// a number of fields all of type double...
double MeasurementA { get; set; }
double MeasurementB { get; set; }
double MeasurementC { get; set; }
}
Given data:
MainClass mainClass = new MainClass();
mainClass.listObj1 = new List<Class1>()
{
new Class1() {
dictObj2 = new Dictionary<int,Class2>() {
{ 1, new Class2() { MeasurementA = 2.0, MeasurementB = 3.0, MeasurementC = 4.0 }},
{ 2, new Class2() { MeasurementA = 5.0, MeasurementB = 6.0, MeasurementC = 7.0 }}
}
}
};
you can write with LINQ:
var fields = typeof(Class2)
// Get Properties of the ClassB
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
// Map each PropertyInfo into collection of its values from c1.dictObj2
.SelectMany(pi => mainClass.listObj1
.SelectMany(c1 => c1.dictObj2)
.Select(p => new
{
Property = pi.Name,
Value = pi.GetValue(p.Value)
}))
// Group data with respect to the PropertyName
.GroupBy(x => x.Property, x => x.Value)
// And create proper dictionary
.ToDictionary(x => x.Key, x => x.ToList());
and now you have a Dictionary with keys of ClassB property names and values as List of those properties values.
Efficiently determine the properties of the inner class (Class 2)
Regardless of efficiency, there really is only one way you can do it (assuming you mean at runtime) and that's using Reflection.
Loop through the properties of the inner class (Class 2)
foreach (var prop in instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
Console.WriteLine(prop.Name);
}
loop through the collection of Class1 objects (listObj1) selecting all instances of the the Class2 property
foreach (var obj in mainClass.listObj1)
{
var innerClasses = obj.dictObj2.Values;
// do something with inner classes
}
output the collection of Class2 property (e.g. the first iteration would return a collection of MeasurementA, one from each Class1 object).
foreach (var prop in typeof(Class2).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
foreach (var obj in mainClass.listObj1)
{
foreach (var innerClass in obj.dictObj2.Values)
{
Console.WriteLine(prop.GetValue(innerClass, null));
}
}
}
Not sure why you are so keen on using LINQ here, I think a couple of simple for loops are all you need here.

Categories