我有ObservableCollection a
和List 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”。
在我的真实代码中a
和b
包含从同一interface
继承的元素,我正在比较该界面中的属性但我无法接受任何它的冒险。
我无法创建新列表,因为它与wpf视图绑定,如果我这样做而不是删除项目,则会出现明显的故障。
有没有更好/更快的方法呢?
答案 0 :(得分:1)
您可以将第二个集合设为HashSet<T>
,以便更快地进行查找。我也改变了你的ForEach
to a 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();
}
}
答案 1 :(得分:0)
我认为最简单的方法是使用相当于List<T>
的{{3}}函数,因为它更通用。而不是
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);
}
}
编辑:
上述扩展方法效率非常低,其他诸如RemoveAll在算法上要快得多。在这种情况下,我不认为它是相关的,因为我们正在讨论可能与视图绑定的ObservableCollection<T>
。鉴于此,我们应该只进行非常少量的更改,否则布局和重新渲染成本将非常高。如果您正在进行大量更改,那么您可能应该使用新的集合替换集合,以便仅重新计算一次布局。