I'm pushing the limits of my knowledge in c# and linq here, so please bear with me if I'm completely off with my example or understanding of linq, c#, generic types, lambda expressions,design patterns, etc.
I have a class that holds two collections, one is the collection to be filtered: IEnumberable<InstagramUser>
and the second is the collection of expressions to filter on: IEnumerable<IInstagramFilter>
.
public class InstagramDisplay {
public IEnumerable<InstagramUser> instagramUsers;
public IEnumerable<IInstagramFilter> instagramFilters;
public InstagramDisplay() {
instagramUsers = new List<InstagramUser>();
instagramFilters = new List<IInstagramFilter>();
}
public IEnumerable<InstagramUser> display() {
instagramFilters.ToList().ForEach(x => instagramUsers.Where(x.filter(instagramUsers)));
return instagramFilters;
}
}
public interface IInstagramFilter {
Expression<Func<T, bool>> filter<T>(IQueryable<T> source);
}
I would have classes extend IInstagramFilter
. Each IInstagramFilter
class would have a property (or function - not sure what's best) that would return the lambda expression that would be applied to IEnumerable<InstagramUser>
in the display()
method.
public class UserFilter : IInstagramFilter {
public Expression<Func<T, bool>> filter<T>(IQueryable<T> source) {
//return some expression - but how?
}
}
I'm struggling to understand a few things:
How to set the expression for each IInstagramFilter
class and then call it in the display()
method?
Each IInstagramFilter
class would have a lambda that would be used to filter IEnumerable<InstagramUser>
but since the Filter class has no knowledge of IEnumerable<InstagramUser>
how would I create the appropriate lambda in the first place?
I think this roughly follows the Decorator Pattern but perhaps there's a better design all together that I'm not aware of.
UPDATED CODE
Based on Olivier's answer this is what I have now. On the return for display()
I'm getting the error when using .Where(filter)
The type arguments for method '
System.Linq.Enumerable.Where<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,bool>)
' cannot be inferred from the usage. Try specifying the type arguments explicitly.
public class InstagramDisplay {
public IEnumerable<InstagramUser> instagramUsers;
public List<Expression<Func<InstagramUser, bool>>> instagramFilters;
public InstagramDisplay() {
instagramUsers = new List<InstagramUser>();
instagramFilters = new List<Expression<Func<InstagramUser, bool>>>();
}
public void addFilter(Expression<Func<InstagramUser, bool>> filter) {
instagramFilters.Add(filter);
}
public IEnumerable<InstagramUser> display() {
return instagramFilters.SelectMany(filter => instagramUsers.Where(filter)).Distinct(); //error on this line
}
}
You have to decide whether you want to perfom some action or whether you want to return something. List<T>.ForEach()
performs an action on each item but has a void return type and does not return anything.
This IInstagramFilter
interface seems superfluous to me. You can declare a filter list like this
var userFilters = new List<Expression<Func<InstagramUser, bool>>>();
userFilters.Add(u => u.Name.StartsWith("A"));
userFilters.Add(u => u.Score >= 100);
If you have a source of users you can do something like this to all users returned by all filters
IQueryable<InstagramUser> usersSource = ...;
// Or IEnumerable<InstagramUser> for LINQ to objects
// if you drop the Expression<> part.
var users = userFilters.SelectMany(f => usersSource.Where(f));
SelectMany
flattens the nested enumerations. The example returns all users whos name starts with "A" or who have a score >= 100. This might return a user twice, therfore I would suggest to append a .Distinct()
var users = userFilters
.SelectMany(f => usersSource.Where(f))
.Distinct();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With