从列表中删除具有重复属性的对象
本文关键字:属性 对象 列表 删除 | 更新日期: 2023-09-27 18:29:47
我在C#中有一个对象列表。所有对象都包含一个属性ID。有几个对象具有相同的ID属性。
如果每个ID属性只有一个对象,我如何修剪列表(或创建新列表)?
[任何额外的重复项都会从列表中删除]
如果您想避免使用第三方库,可以执行以下操作:
var bar = fooArray.GroupBy(x => x.Id).Select(x => x.First()).ToList();
这将根据Id属性对数组进行分组,然后选择分组中的第一个条目。
MoreLINQ DistinctBy()
将完成这项工作,它允许使用对象proeprity来提高清晰度。不幸的是,内置在LINQ Distinct()
中不够灵活。
var uniqueItems = allItems.DistinctBy(i => i.Id);
DistinctBy()
返回给定源的所有不同元素,其中"清晰度"是通过投影和默认设备确定的投影类型的比较器。
- 下载MoreLINQ
- DistinctBy()源
附言:感谢Jon Skeet与社区共享此库
从.NET 6开始,一个新的DistinctBy
LINQ操作符可用:
public static IEnumerable<TSource> DistinctBy<TSource,TKey> (
this IEnumerable<TSource> source,
Func<TSource,TKey> keySelector);
根据指定的键选择器函数从序列中返回不同的元素。
用法示例:
List<Item> distinctList = listWithDuplicates
.DistinctBy(i => i.Id)
.ToList();
还有一个重载具有IEqualityComparer<TKey>
参数。
就地更新:如果不希望创建新的List<T>
,下面是List<T>
类的RemoveDuplicates
扩展方法:
/// <summary>
/// Removes all the elements that are duplicates of previous elements,
/// according to a specified key selector function.
/// </summary>
/// <returns>
/// The number of elements removed.
/// </returns>
public static int RemoveDuplicates<TSource, TKey>(
this List<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> keyComparer = null)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(keySelector);
HashSet<TKey> hashSet = new(keyComparer);
return source.RemoveAll(item => !hashSet.Add(keySelector(item)));
}
这种方法是有效的(O(n)),但也有点危险,因为它基于潜在的破坏性List<T>.RemoveAll
方法。如果keySelector
lambda对某些元素成功,然后对另一个元素失败,则部分修改的List<T>
既不会恢复到其初始状态,也不会处于作为成功的单个Remove
s的结果而可识别的状态。相反,它将转换为损坏状态,其中包括重复出现的现有元素。因此,如果keySelector
lambda不是RemoveDuplicates
方法应在具有丢弃潜在损坏列表的catch
块的try
块中调用。
或者,您可以用一个安全的自定义实现来代替危险的内置RemoveAll
,该实现提供了可预测的行为。
对于所有.NET版本和平台,包括最新的.NET 7。我在GitHub上提交了一份提案,以记录List<T>.RemoveAll
方法的损坏行为,我收到的反馈是,既不应该记录该行为,也不应该修复实现
var list = GetListFromSomeWhere();
var list2 = GetListFromSomeWhere();
list.AddRange(list2);
....
...
var distinctedList = list.DistinctBy(x => x.ID).ToList();
GitHub 上的More LINQ
或者,如果你出于某种原因不想使用外部dll,你可以使用这个Distinct
重载:
public static IEnumerable<TSource> Distinct<TSource>(
this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
用法:
public class FooComparer : IEqualityComparer<Foo>
{
// Products are equal if their names and product numbers are equal.
public bool Equals(Foo x, Foo y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.ID == y.ID
}
}
list.Distinct(new FooComparer());
不确定是否有人仍在寻找其他方法。但我已经使用此代码根据匹配的ID号从用户对象列表中删除了重复项。
private ArrayList RemoveSearchDuplicates(ArrayList SearchResults)
{
ArrayList TempList = new ArrayList();
foreach (User u1 in SearchResults)
{
bool duplicatefound = false;
foreach (User u2 in TempList)
if (u1.ID == u2.ID)
duplicatefound = true;
if (!duplicatefound)
TempList.Add(u1);
}
return TempList;
}
调用:SearchResults = RemoveSearchDuplicates(SearchResults);