Using Except() on a Generic collection
Solution 1
I think the best way would be to define an interface with all of the properties that you want to use in your method. Have the classes that the method may be used in implement this interface. Then, use a generic method and constrain the generic type to derive from the interface.
This example may not do exactly what you want -- it fills in missing dates for items in the list matching a description, but hopefully it will give you the basic idea.
public interface ITransactable
{
string Description { get; }
DateTime? TransactionDate { get; }
}
public class CompletedTransaction : ITransactable
{
...
}
// note conversion to extension method
public static void FillInMissingDates<T>( this IEnumerable<T> collection,
string match,
DateTime defaultDate )
where T : ITransactable
{
foreach (var trans in collection.Where( t => t.Description = match ))
{
if (!trans.TransactionDate.HasValue)
{
trans.TransactionDate = defaultDate;
}
}
}
You'll need to Cast your enumeration to ITransactable before invoking (at least until C# 4.0 comes out).
var list = new List<CompletedTransaction>();
list.Cast<ITransactable>()
.FillInMissingDates("description",DateTime.MinValue);
Alternatively, you could investigate using Dynamic LINQ from the VS2008 Samples collection. This would allow you to specify the name of a property if it's not consistent between classes. You'd probably still need to use reflection to set the property, however.
Solution 2
You could try this approach:
public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection,
Func<T, DateTime> dateProperty, Func<T, string> descriptionProperty, string desc)
{
return collection.Except(collection
.Where(d => descriptionProperty(d) == desc))
.Select(d => dateProperty(d));
}
This allows you to do things like:
someCollection.FillInMissingDates(o => o.CreatedDate, o => o.Description, "matching");
Note that you don't necessarily need the Except()
call, and just have:
.. Where(d => descriptionProperty(d) != desc)
Related videos on Youtube
Christopher Mann
Veteran software engineer and database architect. With more than 15 years of experience I have built solutions for a wide variety of organizations; from federal government agencies in Brazil to one of the largest health care systems in the United States. My specialties are .Net, SQL Server, Business Analytics, and user experience in general. Occasional speaker in code camps and user groups, member of the International .Net Association, president/founder of the Central California .Net User Group, Central California Give Camp, board member of the Central Valley Software Partnership in Central California, member of the International Institute of Business Analysis. I have a Bachelor's Degree in Computer Science and an MBA with emphasis in Information Systems and Entrepreneurship from California State University, Fresno. Microsoft Certified IT Professional (MCITP) in SQL Server Programming and Administration, Microsoft Certified Professional in C#, Certified Qlikview Developer and Designer.
Updated on June 04, 2022Comments
-
Christopher Mann almost 2 years
I have asked this question about using the a Linq method that returns one object (First, Min, Max, etc) from of a generic collection. I now want to be able to use linq's Except() method and I am not sure how to do it. Perhaps the answer is just in front on me but think I need help.
I have a generic method that fills in missing dates for a corresponding descriptive field. This method is declared as below:public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection, string datePropertyName, string descriptionPropertyName) { Type type = typeof(T); PropertyInfo dateProperty = type.GetProperty(datePropertyName); PropertyInfo descriptionProperty = type.GetProperty(descriptionPropertyName); ... }
What I want to accomplish is this. datePropertyName is the name of the date property I will use to fill in my date gaps (adding default object instances for the dates not already present in the collection). If I were dealing with a non-generic class, I would do something like this:
foreach (string description in descriptions) { var missingDates = allDates.Except(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList()); ... }
But how can I do the same using the generic method FillInMissingDates with the dateProperty and descriptionProperty properties resolved in runtime?
-
Noldorin almost 15 yearsYou'll need to show us the definition of your generic class (or at least the relevant bits) before we can properly answer.
-
Christopher Mann almost 15 yearsNoldorin, the type is T, as in the FillInMissingDates generic method declaration.
-
Noldorin almost 15 years@Gustavo: I'm not sure what you mean. If you're not restricting the generic type T, then you can't guarantee the existence of the datePropertyName property.
-
Noldorin almost 15 yearsSorry, still not quite sure what you mean by "do the same". In the example you give, you're using the Except/Where methods. In what respect do you want to do the same thing?
-
Christopher Mann almost 15 yearsI want to use the same Except/Where methods, but the name of the properties is only known at run-time.
-
Jason almost 15 yearsDo you mean that you would like to use syntax like: someCollection.FillInMissingDates(x => x.CreationDate, x => x.ItemDescription) ?
-
-
Noldorin almost 15 yearsThat's not what he's referring to I think. He means that YourClass is generic, not the Except extension method. He even shows in his example type inference makes this unnecessary in his case.
-
Christopher Mann almost 15 yearsYou're right Noldorin. I don't know what is the name of the property Description (thus, the descriptionPropertyName parameter). I've added more information to the question. Thanks.
-
Noldorin almost 15 yearsThis is probably more what he's looking for, though I'm still waiting for clarification.
-
Christopher Mann almost 15 yearsHi tvanfosson. Thanks for your answer. I would not like to use an interface because may not have the ability to the interface to the class definition. As I explained in the question (after I edited), I am using reflection to get the actual properties based on their names. Thanks!