2013-03-21 32 views
31

這可能看起來很傻,但所有的例子,我發現在使用LINQ使用Except兩個列表或僅字符串或整數數組和過濾他們根據比賽,例如:過濾器的LINQ除了在性能

var excludes = users.Except(matches); 

我想用排除,以保持我的代碼短而簡單,但似乎無法找出如何做到以下幾點:

class AppMeta 
{ 
    public int Id { get; set; } 
} 

var excludedAppIds = new List<int> {2, 3, 5, 6}; 
var unfilteredApps = new List<AppMeta> 
         { 
          new AppMeta {Id = 1}, 
          new AppMeta {Id = 2}, 
          new AppMeta {Id = 3}, 
          new AppMeta {Id = 4}, 
          new AppMeta {Id = 5} 
         } 

我怎樣可以過濾對AppMeta回列表excludedAppIds

回答

49

嘗試一個簡單的地方查詢

var filtered = unfilteredApps.Where(i => !excludedAppIds.Contains(i.Id)); 

的只是方法使用平等,你的列表中包含不同類型的對象,所以沒有一個項目所包含將等於!

+1

爲了提高效率,建議存儲'excludedAppIds'作爲'HashSet',否則你有一個'O(N²)'算法遍歷你的排除列表的次數與源代碼中的元素一樣多。 – 2017-08-08 11:23:17

6

從排除列表構造一個List<AppMeta>並使用Except Linq運算符。

var ex = excludedAppIds.Select(x => new AppMeta{Id = x}).ToList();       
var result = ex.Except(unfilteredApps).ToList(); 
+0

該解決方案允許使用大型數據集+1 – Kharaone 2014-08-26 15:08:05

+0

這需要太多內存和時間。 – 2017-03-28 08:36:29

11

ColinE的回答簡單而優雅。如果您的列表較大,並且排除了排除的應用程序列表,則BinarySearch<T>可能會比Contains更快。

例:

unfilteredApps.Where(i => excludedAppIds.BinarySearch(i.Id) < 0); 
+0

這非常有幫助,謝謝。在這種情況下,它們既不大也不排除被排除的列表,但我將把它歸檔併發送upvotes你的方向。 – Wesley 2013-03-21 06:42:47

+0

謝謝一堆。我會爲未來的讀者發佈一個例子。 – dotNET 2013-03-21 06:47:13

+1

'!(a> = b)'是說'a jazzcat 2016-09-02 16:03:38

10

我使用除擴展方法,它允許你用橙子,只要它們都具有共同的東西,可以用來對它們進行比較比較蘋果,像一個標識或鑰匙。

public static class ExtensionMethods 
{ 
    public static IEnumerable<TA> Except<TA, TB, TK>(
     this IEnumerable<TA> a, 
     IEnumerable<TB> b, 
     Func<TA, TK> selectKeyA, 
     Func<TB, TK> selectKeyB, 
     IEqualityComparer<TK> comparer = null) 
    { 
     return a.Where(aItem => !b.Select(bItem => selectKeyB(bItem)).Contains(selectKeyA(aItem), comparer)); 
    } 
} 

然後使用它是這樣的:

var filteredApps = unfilteredApps.Except(excludedAppIds, a => a.Id, b => b); 

擴展非常相似,可啉的答案,它只是打包成一個整齊的擴展,它可以在不太多的精神開銷被重用。

11

這是LINQ需要

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKey) 
{ 
    return from item in items 
      join otherItem in other on getKey(item) 
      equals getKey(otherItem) into tempItems 
      from temp in tempItems.DefaultIfEmpty() 
      where ReferenceEquals(null, temp) || temp.Equals(default(T)) 
      select item; 
} 
+1

這將是一個很好的擴展方法 – Wesley 2015-04-20 16:31:07

+1

它的工作原理非常好... – azuneca 2015-06-16 11:40:50

+1

這很棒,除非我不確定連接是排除匹配的最有效方法,並且對於雙方都使用相同的getKey, t爲原始問題工作。 – NetMage 2016-11-01 23:15:24

-3
public static class ExceptByProperty 
{ 
    public static List<T> ExceptBYProperty<T, TProperty>(this List<T> list, List<T> list2, Expression<Func<T, TProperty>> propertyLambda) 
    { 
     Type type = typeof(T); 

     MemberExpression member = propertyLambda.Body as MemberExpression; 

     if (member == null) 
      throw new ArgumentException(string.Format(
       "Expression '{0}' refers to a method, not a property.", 
       propertyLambda.ToString())); 

     PropertyInfo propInfo = member.Member as PropertyInfo; 
     if (propInfo == null) 
      throw new ArgumentException(string.Format(
       "Expression '{0}' refers to a field, not a property.", 
       propertyLambda.ToString())); 

     if (type != propInfo.ReflectedType && 
      !type.IsSubclassOf(propInfo.ReflectedType)) 
      throw new ArgumentException(string.Format(
       "Expresion '{0}' refers to a property that is not from type {1}.", 
       propertyLambda.ToString(), 
       type)); 
     Func<T, TProperty> func = propertyLambda.Compile(); 
     var ids = list2.Select<T, TProperty>(x => func(x)).ToArray(); 
     return list.Where(i => !ids.Contains(((TProperty)propInfo.GetValue(i, null)))).ToList(); 
    } 
} 

public class testClass 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 
} 

爲了測試此:

 List<testClass> a = new List<testClass>(); 
     List<testClass> b = new List<testClass>(); 
     a.Add(new testClass() { ID = 1 }); 
     a.Add(new testClass() { ID = 2 }); 
     a.Add(new testClass() { ID = 3 }); 
     a.Add(new testClass() { ID = 4 }); 
     a.Add(new testClass() { ID = 5 }); 

     b.Add(new testClass() { ID = 3 }); 
     b.Add(new testClass() { ID = 5 }); 
     a.Select<testClass, int>(x => x.ID); 

     var items = a.ExceptBYProperty(b, u => u.ID); 
+0

反思應該永遠是你的最後手段,對於一個根本不需要它的簡單任務來說,這是很多反思。 – NetMage 2017-03-09 22:53:37

1

我喜歡的除了擴展方法,但原來的問題沒有對稱密鑰訪問和I更喜歡包含(或任何變體)加入,所以所有功勞azuneca's answer

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<TKey> items, 
    IEnumerable<T> other, Func<T, TKey> getKey) { 

    return from item in items 
     where !other.Contains(getKey(item)) 
     select item; 
} 

然後可以像使用:

var filteredApps = unfilteredApps.Except(excludedAppIds, ua => ua.Id); 

此外,這個版本允許需要對異常的映射IEnumerable的使用選擇:

var filteredApps = unfilteredApps.Except(excludedApps.Select(a => a.Id), ua => ua.Id);