我有一个基本代码和一系列位置的人员列表。我需要消除列表中具有相同位置的不同基本代码的人员,并保留具有不同位置的人员。
我尝试使用IEqualityComparer,并在linq中分组,但我没有成功。 你能告诉我怎么做吗? 这是我的班级结构
public class Person
{
public string Name { get; set; }
public List<Location> Locations { get; set; }
}
public class Location
{
public string Name { get; set; }
public string BaseCode { get; set; }
}
数据示例
Person 1
Name : John
Locations :
[0] Name : India , BaseCode : "AA12"
[1] Name : USA ,BaseCode : "AA14"
Person 2
Name : John
Locations :
[0] Name : India, BaseCode : "AA13"
[1] Name : USA ,BaseCode : "AA14"
Person 3
Name : John
Locations :
[0] Name : India, BaseCode : "AA16"
[1] Name : UK , BaseCode : "AA17"
我想从我的列表中过滤第2个人并保留第1个人和第3个人。请提供建议
答案 0 :(得分:1)
免责声明:此解决方案并未专门处理具有不同/相同位置的相同BaseCode
;你没有在要求中提到任何相关内容。
IEqualityComparer<T>
路线
此处的重要部分是IEqualityComparer<T>
和Person
的{{1}}实施:
Location
class Program
{
static void Main(string[] args)
{
var p1 = new Person {Name ="John", BaseCode="AA12", Locations = new List<Location>
{
new Location { Name = "India" },
new Location { Name = "USA" }
}};
var p2 = new Person {Name ="John", BaseCode="AA13", Locations = new List<Location>
{
new Location { Name = "India" },
new Location { Name = "USA" }
}};
var p3 = new Person {Name ="John", BaseCode="AA14", Locations = new List<Location>
{
new Location { Name = "India" },
new Location { Name = "UK" }
}};
var persons = new List<Person> { p1, p2, p3 };
// Will not return p2.
var distinctPersons = persons.Distinct(new PersonComparer()).ToList();
Console.ReadLine();
}
}
public class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null || y == null)
return false;
bool samePerson = x.Name == y.Name;
bool sameLocations = !x.Locations
.Except(y.Locations, new LocationComparer())
.Any();
return samePerson && sameLocations;
}
public int GetHashCode(Person obj)
{
return obj.Name.GetHashCode();
}
}
public class LocationComparer : IEqualityComparer<Location>
{
public bool Equals(Location x, Location y)
{
if (x == null || y == null)
return false;
return x.Name == y.Name;
}
public int GetHashCode(Location obj)
{
return obj.Name.GetHashCode();
}
}
使用linq PersonComparer
扩展名提供Except
来生成两个位置列表之间的差异列表。
LocationComparer
然后输入linq PersonComparer
方法。
Distinct
路线
如果您需要使用IEquatable<T>
不同计入“匹配”,我认为此路由不会因为BaseCode
而有效没有给你机会来区分价值观。
另一种解决方案是对类本身实施GetHashCode
并覆盖IEquatable<T>
,GetHashCode
和Distinct
将遵循此实现:
Except
这导致更简单的呼叫:
public class Person : IEquatable<Person>
{
public string Name { get; set; }
public string BaseCode { get; set; }
public List<Location> Locations { get; set; }
public bool Equals(Person other)
{
if (other == null)
return false;
bool samePerson = Name == other.Name;
// This is simpler because of IEquatable<Location>
bool sameLocations = !Locations.Except(other.Locations).Any();
return samePerson && sameLocations;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
public class Location : IEquatable<Location>
{
public string Name { get; set; }
public bool Equals(Location other)
{
if (other == null)
return false;
return Name == other.Name;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
答案 1 :(得分:0)
您可以使用IEquatable接口,并覆盖Equal和GetHashCode方法,如下所示:
在问题变更后编辑:
public class Location : IEquatable<Location>
{
public string Name { get; set; }
public string BaseCode { get; set;
public bool Equals(Location other)
{
if (Object.ReferenceEquals(other, null)) return false;
if (Object.ReferenceEquals(this, other)) return true;
return BaseCode.Equals(other.BaseCode);
}
public override int GetHashCode()
{
return BaseCode.GetHashCode();
}
}
所以,现在你可以在Person列表中使用Distinct,它只会返回不同的名称和BaseCode。
var distinctListPerson = PersonList.Distinct().ToList();
您可以从MSDN
了解此界面答案 2 :(得分:0)
亚当的解决方案是处理它的更“恰当”的方式。但是如果你想用LINQ来做,那么这样的事情也应该这样做(注意代码需要排序位置并将字符串作为标识符):
persons
.GroupBy(x => x.Name)
.SelectMany(x => x)
.GroupBy(y => string.Concat(y.Locations.Select(z => z.Name)))
.SelectMany(x => x
.GroupBy(y => string.Concat(y.Locations.Select(z => z.BaseCode)))
.Select(x => x.First());
答案 3 :(得分:0)
我很想写下面的内容。我没有检查过y.Locations.Equals()
是否有效,但是为了做同样的工作而更换它应该很简单。
List<Person> personList = new List<Person>();
List<Person> deduplicatedPersonList = new List<Person>();
personList.ForEach(x =>
{
Person existingPerson = personList.Find(y =>
{
if (y.Locations.Equals(x.Locations))
return false;
return true;
});
if (existingPerson == null)
deduplicatedPersonList.Add(x);
});