从列表中删除具有重复属性的对象

本文关键字:属性 对象 列表 删除 | 更新日期: 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>既不会恢复到其初始状态,也不会处于作为成功的单个Removes的结果而可识别的状态。相反,它将转换为损坏状态,其中包括重复出现的现有元素。因此,如果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);

相关文章: