0

I am trying to build a generic Filter Control that displays unique values in the data grid, and lets the user filter the unique values for a particular column in the grid.

I liked the answers proposed for this question, however all of those require you to know the property before hand. I would like to make the code OCP compliant and have just one method that takes the property name string and applies distinct function on it (maybe using reflection). What is the best solution to this problem, considering my columns (thereby column names to filter, like Name, Age, etc. are dynamic).

Specifically, I am trying to avoid this switch case statement:

switch (listColumn.DisplayMemberPath)
{
    case "Name" :
            listColumn.Items = GridItems.GroupBy(item => item.Name).Select(item => item.First());
            break;

    case "Age" :
            listColumn.Items = GridItems.GroupBy(item=> item.Age).Select(item => item.First());
            break;

    // and so on...
 }

and have a generic method like this:

public IEnumerable GetDistinctValues(IEnumerable gridItems, string propertyName)
{
    // logic to return the distinct values for the given property name...
}

FYI - My collection in the ViewModel is of type ICollectionView (guessing that there is something similar already defined in the CollectionView that does the kind of filtering I am looking for).

Community
  • 1
  • 1
Shankar Raju
  • 4,356
  • 6
  • 33
  • 52
  • You're basically looking for a `DistinctBy` method. Check out the accepted answer [here](http://stackoverflow.com/questions/489258/linq-distinct-on-a-particular-property). – Neil Smith Aug 19 '14 at 23:30
  • Sorry I don't know if the accepted answer would work for me. Please see my question: I need to be able to apply distinct by using a property name and so I cannot pass object.property to this method. – Shankar Raju Aug 20 '14 at 01:10

2 Answers2

0

You can use expression tree feature of C# like below.

public static IEnumerable GetDistinctValues<T>(IEnumerable<T> gridItems, string propertyName) where T : class
{
    // logic to return the distinct values for the given property name...
    var xpre = GetPropertyExpression<T>(typeof(T), propertyName);
    return gridItems.GroupBy(item => xpre).Select(item => item.First());
}

private static Func<T, object> GetPropertyExpression<T>(Type type, string propertyName)
{
    ParameterExpression parameter = Expression.Parameter(type, "x");
    MemberExpression propertyExpr = GetPropertyExpression(parameter, type, propertyName);

    if (propertyExpr == null)
        return null;

    Expression<Func<T, object>> expression = Expression.Lambda<Func<T, object>>(Expression.Convert(propertyExpr, typeof(object)), new ParameterExpression[1] { parameter });
    return expression.Compile();
}

private static MemberExpression GetPropertyExpression(Expression param, Type type, string propertyName)
{
    var property = type.GetProperty(propertyName);

    if (property == null)
    {
        if (propertyName.Contains("_") || propertyName.Contains("."))
        {
            var innerProps = propertyName.Split(new char[] { '_', '.' }, 2);
            property = type.GetProperty(innerProps[0]);

            if (property != null)
            {
                var pe = Expression.Property(param, property);
                return GetPropertyExpression(pe, property.PropertyType, innerProps[1]);
            }
            else
            {
                return null;
            }
        }
    }
    else
    {
        return Expression.Property(param, property);
    }

    return param as MemberExpression;
}

Usage:

var lst = new List<Student>();
lst.Add(new Student { Name = "Joe", Age = 23 });
lst.Add(new Student { Name = "John", Age = 28 });
lst.Add(new Student { Name = "Jane", Age = 21 });
lst.Add(new Student { Name = "John", Age = 15 });

var vals = GetDistinctValues(lst, "Name"); // here
cackharot
  • 688
  • 1
  • 6
  • 13
-1

You can do something like this:

void GetDistinctValues(string aPropName)
{
    var props = typeof(A).GetProperties();

    // make sure your property exists here, otherwise return

    // Something like that should be what you want:
    var return_col = gridItems[aPropName].Distinct();
}

public class A {
    public int Age{get;set;}
    public int Height{get;set;}
}

Basically, make sure that column / property exist, and then simply run a linq distinct on that.


Regarding your comment, you pass in an IEnumerable, so you can call Distinct on it:

var nmrbl = Enumerable.Range(1,10).Select (e => e%3);
// nmrbl={ 1 ,2 ,0 ,1 ,2 ,0 ,1 ,2 ,0 ,1}
var dist = nmrbl.Distinct();
// dist = {1,2,0}
Noctis
  • 11,507
  • 3
  • 43
  • 82
  • I did not understand how you can run Distinct with this statement: var return_col = gridItems[aPropName].Distinct(); Am I missing something? – Shankar Raju Aug 20 '14 at 01:05