Reorder list by a property I don't have - c#

I have a bog-standard list in C#: List (where Channel is a TV channel). Channel is defined as:
public class Channel
{
public int ChannelId { get; set; }
public string ChannelName { get; set; }
}
My ORM populates a list of Channels, which comes from the database in Channel Id order. I need to re-order the list based on a new custom sort property, say ChannelOrder, but I can't modify the underlying database.
Any thoughts on how to do this, without modifying the underlying DB?
So for instance, if I current have coming from the db:
ChannelId =1, ChannelName = "BBC1", ChannelId =2, ChannelName = "BBC2", ChannelId =3, ChannelName = "ITV"
I might want them ordered as BBC2, BBC1, ITV, basically a custom order.

If you're happy to do this in-process (rather than as part of the query) you can use:
var orderedChannelNames = new[] { "BBC2", "BBC1", "ITV", ... };
var sorted = unsorted.OrderBy(ch => orderedChannelNames.IndexOf(ch.ChannelName));
Alternatively, if you know all the channels in your list will be present:
var map = unsorted.ToDictionary(ch => ch.ChannelName);
var sorted = orderedChannelNames.Select(name => map[name]);

You could maybe use another class that returns an "attached" property, kind of like attached properties in WPF:
public static class ChannelSort
{
static Dictionary<Channel, int> _dict = new Dictionary<Channel, int>();
public static int GetSort(this Channel c)
{
return _dict[c] //will throw if key's not found,
//may want to handle it for more descriptive exception
}
public static int SetSort(this Channel c, int sort)
{
_dict[c] = sort;
}
}
And then you could do this:
var result = unsortedList.OrderBy(c => c.GetSort());

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.

Fastest way of comparing two lists without using nested loops

I have two types:
public class SubCategories
{
public static List<SubCategories> subCategories = new List<SubCategories>();
public string title { get; set; }
public string IDfromCategories { get; set; }
public string subCategoryID { get; set; }
public bool isChecked { get; set; }
}
public class UserInsideCategories
{
public string userEmail { get; set; }
public string iDfromSubCategories { get; set; }
}
And two lists both containing this object multiple times.
Now I wanna go through a list with type SubCategories and check each object, if it contains the same value as my other list of type UserInsideCategories. Specifically, I wanna know if any object on the list.SubcategoryID is equal to any object on the other list.IdFromSubCateogires.
I achieved this like so:
List<SubCategories> branch = new List<SubCategories>();
for(int i = 0; i < subCategories.Count; i++)
{
SubCategories e = new SubCategories();
for(int x = 0; x < allSubs.Count; x++)
{
if (e.IDfromCategories == allSubs[x].iDfromSubCategories)
e.isChecked = true;
}
branch.Add(e);
}
So I am using a nested loop. But since I have to do this multiple times, it takes far too long.
I also thought about turning all values from SubCategories into a simple string array and use the Contains function, to see if the current object.IDfromCategories contains the object on the array. This would mean I would NOT use a for loop. But interenally, I believe, the system is still using a loop and therefore there would be no performance benefit.
What would be the best way of checking each object if it contains a value from the other list?
You should use some kind of lookup table. Probably either HashSet or Dictionary. The former only allows checking if a key exists in the set, while the later allows you to also find the object the key belongs to.
To check all the UserInsideCategories that shares an id with a SubCategories you could write:
var dict = subCategoriesList.ToDictionary(s => s.subCategoryID, s => s);
var matches = userInsideCategoriesList.Where(l => dict.ContainsKey(l.iDfromSubCategories));
if you want matching pairs you could write:
foreach (var user in userInsideCategoriesList)
{
if (dict.TryGetValue(user.iDfromSubCategories, out var subCategory))
{
// Handle matched pairs
}
}
This assumes that the ID is unique in respective list. If you have duplicates you would need something like a multi-value dictionary. There are no multi-value dictionary built in, but I would expect there are some implementations if you search around a bit.

Invoke a static class list by reflection C#

I have a series of C# static lists in an API project that are very similar to the simple example defined here.
using System.Collections.Generic;
namespace myproject.api.PropModels
{
public class CommonSelectOptionsYesNoItem
{
public int Id { get; set; }
public string Title { get; set; }
}
public static class CommonSelectOptionsYesNo
{
public static readonly List<CommonSelectOptionsYesNoItem> Table = new List<CommonSelectOptionsYesNoItem>
{
new CommonSelectOptionsYesNoItem { Id = 0, Title = "No",},
new CommonSelectOptionsYesNoItem { Id = 1, Title = "Yes",},
};
}
}
These models establish a common information reference between a Javascript web application and the API that services the application.
User's are uploading spreadsheet data to the API that includes the list class name and the Title of an item in the list. I need to be able to determine what Id is associated with the Title - if any.
For example I know that this the information is in the list CommonSelectOptionsYesNo.Table and the Title property is "Yes". I can therefore determine that the the Id is 1.
In principle I can set up a switch / case method that picks the list identified as CommonSelectOptionsYesNo.Table and then gets the Id value. There are however than 60 of these reference lists and they keep growing.
Can I use reflection to invoke an instance of the readonly static list based on the the static class object name - in this example CommonSelectOptionsYesNo.Table?
After further research have worked out the following method to call up the static readonly list and return the Id for any given "Title" value.
The propModelKey is stored with the static list class in a dictionary of all the lists.
The list can be extracted as an object - knowing that the list is always declared with the property name "Table".
The properties of the list objects can vary depending on the purpose of the list but they always have the "Id" and "Title" properties. Serializing and deserializing the object with the simple class object "SelectOptions" generates a list that can be queried to extract the Id corresponding to the Title string submitted.
// This will return an Id of 1 from the simple YesNo list
var id = GetSelectListIndex("QuestionOneId", "Yes");
// Method to extract the Id of a value in a list given the list model key
private static int? GetSelectListIndex(string propModelKey, string title)
{
if (SelectListModelMap.ContainsKey(propModelKey))
{
var model = SelectListModelMap[propModelKey];
var typeInfo = Type.GetType("myproject.api.PropModels." + model).GetTypeInfo();
var fieldInfo = typeInfo.DeclaredFields.FirstOrDefault(x => x.Name == "Table");
var json = JsonConvert.SerializeObject(fieldInfo.GetValue(new object()));
var dictionary = JsonConvert.DeserializeObject<List<SelectOptions>>(json);
var index = dictionary.FirstOrDefault(x => x.Title == title)?.Id;
return index;
}
return null;
}
// Dictionary of lists with model key and class name
public static Dictionary<string, string> SelectListModelMap => new Dictionary<string, string>
{
{ "QuestionOneId", "CommonSelectOptionsYesNo" },
{ "CountryId", "CommonSelectOptionsCountries" },
// ... other lists
};
// generic class to extract the Id / Title pairs
public class SelectOptions
{
public int Id { get; set; }
public string Title { get; set; }
}

LINQ join based in interface implemation

Let's say I have a an interface, which is basically a combination of two sub-interfaces. The idea behind this is, that I have two different API's. One which provides public information on a person. And once which provides the 'secret' information. It could look something like this:
public interface IPublicPersonData
{
// The ID is the key
int PersonId { get; set; }
// This property is specific to this part
string Name {get; set; }
}
public interface ISecretPersonData
{
// The ID is the key
int PersonId { get; set; }
// This property is specific to this part
decimal AnnualSalary{ get; set; }
}
public interface IPerson: IPublicPersonData, ISecretPersonData
{
// No new stuff, this is merely a combination of the two.
}
So basically I get two lists. One List<IPublicPersonData> and one List<ISecretPersonData>. I would like to join these into a single List<IPerson>, ideally using LINQ.
I cannot really find anything on how control the type of output from LINQ, based on the type of input, even if the logic is there (in the means of interfaces implementing interfaces).
public List<IPerson> JoinPersonData(
List<IPublicPersonData> publicData,
List<ISecretPersonData> secretData)
{
// What the heck goes here?
}
Say you wrote a method such as:
public ISomething CombinePersonWithSecret(
IPublicPersonData publicPerson,
ISecretPersonData secret)
{
if(publicPerson.PersonId != secret.PersonId)
{
throw ...;
}
//join 2 params into a single entity
return something;
}
Now you might...
IEnumerable<ISomething> secretivePeople = PublicPeople.Join(
SecretPersonData,
publicPerson => publicPerson.PersonId,
secret => secret.PersonId,
(publicPerson, secret) => CombinePersonWithSecret(publicPerson, secret))
The problem is not in the Join, it is in the IPerson you want to return. One of the parameters of the Join methods is used what to do with joined result.
You want to join them into a new object that implements IPerson. If you already have such an object: great, use that one, if you don't have it, here is an easy one:
public PersonData : IPerson // and thus also IPublicPersonData and ISecretPersonData
{
// this PersonData contains both public and secret data:
public IPublicPersonData PublicPersonData {get; set;}
public ISecretPersnData SecretPersonData {get; set;}
// implementation of IPerson / IPublicPersonData / ISecretPersonData
int PersonId
{
get {return this.PublicPersonData.Id; }
set
{ // update both Ids
this.PublicPersonData.Id = value;
this.SecreatPersonData.Id = value;
}
}
public string Name
{
get { return this.PublicPersonData.Name; },
set {this.PublicPersonData.Name = value;}
}
public decimal AnnualSalary
{
get {return this.SecretPersonData.AnnualSalary;},
set {this.SecretPersnData.AnnualSalary = value;
}
}
This object requires no copying of the values of the puclic and secret person data. Keep in mind however, if you change values, the original data is changed. If you don't want this, you'll need to copy the data when creating the object
IEnumerable<IPublicPersonData> publicData = ...
IEnumerable<ISecretPersonData> secretData = ...
// Join these two sequences on same Id. Return as an IPerson
IEnumerable<IPerson> joinedPerson = publicData // take the public data
.Join(secretData, // inner join with secret data
publicPerson => publicPerson.Id, // from every public data take the Id
secretPerson => secretPerson.Id, // from every secret data take the Id
(publicPerson, secretPerson) => new PersonData() // when they match make a new PersonData
{
PublicPersonData = publicPerson,
SecretPersnData = secretPerson,
});
LINQ's Join method does the job for you. Assuming there is a Person : IPerson class, here is two ways to implement your JoinPersonData method:
public static IEnumerable<IPerson> LiteralJoinPersonData(List<IPublicPersonData> publics, List<ISecretPersonData> secrets)
{
return from p in publics
join s in secrets on p.PersonId equals s.PersonId
select new Person(p.PersonId, p.Name, s.AnnualSalary);
}
public static IEnumerable<IPerson> FunctionalJoinPersonData(List<IPublicPersonData> publics, List<ISecretPersonData> secrets)
{
return publics
.Join<IPublicPersonData, ISecretPersonData, int, IPerson>(
secrets,
p => p.PersonId,
s => s.PersonId,
(p, s) => new Person(p.PersonId, p.Name, s.AnnualSalary));
}

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;
}
}
}

Categories