根据列表属性删除重复的对象

时间:2015-11-21 03:23:08

标签: c# .net linq generics reflection

我想基于某些属性区分对象列表。这些属性是通过反射和一些条件得到的。我搜索了很多但是找不到能够在这个lambda表达式中循环的任何代码片段或解决方案。

List<PropertyInfo> propList = ... 
var distinctList = FullList
  .GroupBy(uniqueObj => 
  { 
  //do a loop to iterate all elements in propList 
  })
  .Select(x => x.First());

2 个答案:

答案 0 :(得分:1)

您可以使用此方法使用属性名称创建表达式:

public static Expression<Func<T, object>> GetPropertySelector<T>(string propertyName)
{
    var arg = Expression.Parameter(typeof(T), "x");
    var property = Expression.Property(arg, propertyName);
    //return the property as object
    var conv = Expression.Convert(property, typeof(object));
    var exp = Expression.Lambda<Func<T, object>>(conv, new ParameterExpression[] { arg });
    return exp;
}

并像这样使用:

var exp = GetPropertySelector<Person>("PropertyName");

现在你可以轻松搞清楚了:

List<Person> distinctPeople = allPeople
  .GroupBy(exp.Compile())
  .Select(g => g.First())
  .ToList();

答案 1 :(得分:1)

好的,我花了一段时间才想到这一点。

基本上,您可以使用Linq GroupBy运算符,但是您需要使用接受自定义IEQualityComparer的重载,因为您要根据所有对象的子集验证对象的相等性属性。

属性子集存储在您在代码中的其他位置创建的List<PropertyInfo>中,或者您从服务或其他任何位置创建的内容。

因此,实施IEqualityComparer,然后将其与GroupBy

一起使用
//Dummy class representing your data.
//
//Notice that I made the IEqualityComparer as a child class only
//for the sake of demonstration

public class DataObject 
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int Grade { get; set; }

    public static List<PropertyInfo> GetProps()
    {
        //Only return a subset of the DataObject class properties, simulating your List<PropertyInfo>
        return typeof(DataObject).GetProperties().Where(p => p.Name == "Name" || p.Name == "Grade").ToList();
    }


    public class DataObjectComparer : IEqualityComparer<DataObject>
    {
        public bool Equals(DataObject x, DataObject y)
        {
            if (x == null || y == null)
                return false;

            foreach (PropertyInfo pi in DataObject.GetProps())
            {
                if (!pi.GetValue(x).Equals(pi.GetValue(y)))
                    return false;
            }
            return true;
        }

        public int GetHashCode(DataObject obj)
        {
            int hash = 17;

            foreach (PropertyInfo pi in DataObject.GetProps())
            {
                hash = hash * 31 + pi.GetValue(obj).GetHashCode();
            }

            return hash;
        }
    }
}


//Then use that in your code:
//

List<DataObject> lst = new List<DataObject>();
lst.Add(new DataObject { Name = "Luc", Age = 49, Grade = 100 });
lst.Add(new DataObject { Name = "Luc", Age = 23, Grade = 100 });
lst.Add(new DataObject { Name = "Dan", Age = 49, Grade = 100 });
lst.Add(new DataObject { Name = "Dan", Age = 23, Grade = 100 });
lst.Add(new DataObject { Name = "Luc", Age = 20, Grade = 80 });

List<DataObject> dist = lst.GroupBy(p => p, new DataObject.DataObjectComparer()).Select(g => g.First()).ToList();    
//The resulting list now contains distinct objects based on the `Name` and `Grade` properties only.

我希望这有助于您更接近解决方案。

干杯