从可观察集合中删除名称出现在其他列表中的项目

本文关键字:其他 列表 项目 观察 集合 删除 | 更新日期: 2023-09-27 18:37:04

我有ObservableCollection aList b

现在我想从集合中删除a在列表b中具有等效元素的元素。

我此刻的代码:

public static void CrossRemove<TFirst, TSecond>(this ObservableCollection<TFirst> collection, IEnumerable<TSecond> secondCollection, Func<TFirst, TSecond, bool> predicate)
{
    collection.Where(first => secondCollection.Any(second => predicate(first, second)))
        .ToList().ForEach(item => collection.Remove(item));
}

用法:

ObservableCollection<string> first = new ObservableCollection<string> { "1", "2", "3", "4", "5", "6", "k" };
IEnumerable<int> second = new List<int> { 2, 3, 5 };
first.CrossRemove(second, (x, y) => x == y.ToString());

此代码从集合中删除"2"、"3"和"5",留下"1"、"4"、"6"和"K"。

在我的真实代码中,ab包含从同一interface继承的元素,我正在比较该接口中的属性,但我无法冒险使用它。

我无法创建新列表,因为它绑定到 wpf 视图,如果我这样做而不是删除项目,会出现明显的故障。

有没有更好/更快的方法来做到这一点?

从可观察集合中删除名称出现在其他列表中的项目

您可以将第二个集合设置为HashSet<T>,以便更快地查找。我还把你的ForEach改成了foreach.这更容易用属性来演示,就像在原始属性中一样。

void Main()
{
    ObservableCollection<MyClass> first = new ObservableCollection<MyClass> { "1", "2", "3", "4", "5", "6", "k" };
    ISet<IMyInterface> second = new HashSet<IMyInterface>(new MyClass2[] { 2, 3, 5 }, new MyEqualityComparer());
    first.CrossRemove(second);
    Console.WriteLine(string.Join(", ", first.Select(x => x.MyProperty)));
    // 1, 4, 6, k
}
public interface IMyInterface
{
    string MyProperty { get; set; }
}
public class MyEqualityComparer : IEqualityComparer<IMyInterface>
{
    public bool Equals(IMyInterface a, IMyInterface b)
    {
        return a.MyProperty == b.MyProperty;
    }
    public int GetHashCode(IMyInterface obj)
    {
        return obj.MyProperty.GetHashCode();
    }
}
public static class Extensions
{
    public static void CrossRemove<TFirst, TSecond>(this ObservableCollection<TFirst> collection, ISet<TSecond> set) where TFirst : TSecond
    {
        foreach (var item in collection.Where(item => set.Contains(item)).ToList())
            collection.Remove(item);
    }
}
public class MyClass : IMyInterface
{
    public string MyProperty { get; set; }
    public static implicit operator MyClass(string s)
    {
        return new MyClass { MyProperty = s };
    }
}
public class MyClass2 : IMyInterface
{
    public string MyProperty { get; set; }
    public static implicit operator MyClass2(int i)
    {
        return new MyClass2 { MyProperty = i.ToString() };
    }
}

即使对象不共享公共接口,您也应该能够编写一个正确处理两者的IEqualityComparer<object>,例如,如果您的 lambda 谓词是:

(TypeA a, TypeB b) => a.PropA == b.PropB

那么你的班级将是:

public class MyOtherEqualityComparer : IEqualityComparer<object>
{
    private object GetProperty(object obj)
    {
        if (obj is TypeA)
            return ((TypeA)obj).PropA;
        else if (obj is TypeB)
            return ((TypeB)obj).PropB;
        else
            throw new Exception();
    }
    public bool Equals(object a, object b)
    {
        return GetProperty(a).Equals(GetProperty(b));
    }
    public int GetHashCode(object obj)
    {
        return GetProperty(obj).GetHashCode();
    }
}
我认为最简单的

方法是使用List<T>的RemoveAll函数,因为它更通用。而不是

first.CrossRemove(second, (x, y) => x == y.ToString());

我会写

first.RemoveAll(item1 => second.Any(item2 => item1 == item2.ToString()));

不幸的是,ObservableCollection<T>没有这个方法,所以我们需要写一个:

public static class Extensions
{
    public static void RemoveAll<T>(this ICollection<T> collection, Func<T, bool> pred)
    {
        var toBeRemoved = collection.Where(pred).ToArray();
        foreach (var item in toBeRemoved)
            collection.Remove(item);
    }
}

编辑:

上面的扩展方法

效率非常低,其他像这样的扩展方法在算法上要快得多。不过,在这种情况下,我认为这无关紧要,因为我们谈论的是一个可能与视图绑定的ObservableCollection<T>。鉴于此,我们应该只做非常少量的更改,否则布局和重新渲染成本将非常高。如果要进行大量更改,则可能应该将集合替换为新集合,以便仅重新计算一次布局。