2013-10-28 23 views
4

对我有ObservableCollection aList b从名字出现其他列表

现在我想从集合中删除有其表b相当于a元素的ObservableCollection项目中移除。

我的代码在这一刻:

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视图,并且如果我这样做而不是删除项目,将会出现可见的毛刺。

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

+0

你的问题到底是什么? –

+0

抱歉,更新 – Adassko

+1

我想不出一个更好的方法。你必须坚持这种方法。可能有人带来更好的解决方案:) –

回答

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>与两个工作,例如如果你的拉姆达谓词会一直:

(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(); 
    } 
} 
+0

感谢您抽出时间,我会很好地利用这个:)最后一件事:你能想到一个比“CrossRemove”更好的名字,因为它不会说太多,英文是不是我的母语;) – Adassko

+1

有几个名字可以使用:'差异','补充'或'减法'(来自[集合的数学思想](http://mathworld.wolfram.com/SetDifference)。 ),或'RemoveAll'像'List '的方法(除了需要一个谓词,而不是一个集合)或'RemoveWhere''就像'HashSet ''的方法(它也需要一个谓词)。我想我会用'Subtract'或'RemoveAll'去。 –

0

我认为要做到这一点最简单的方法是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); 
    } 
} 

编辑:

以上扩展方法效率很低,其他算法如this one算得很多 更快。但在这种情况下,我认为这并不重要,因为我们正在讨论大概与观点相关的ObservableCollection<T>。鉴于此,我们应该只做很少的更改,否则布局和重新渲染成本会非常高。如果您正在进行大量更改,那么您应该用新的更换集合,以便布局仅重新计算一次。