Filtrer Linq SAUF sur les propriétés

Cela peut sembler idiot, mais tous les exemples que j’ai trouvés pour utiliser Except dans linq utilisent deux listes ou tableaux de chaînes ou d’entiers uniquement et les filtrent en fonction des correspondances, par exemple:

 var excludes = users.Except(matches); 

Je souhaite utiliser exclude pour que mon code soit simple et bref, mais je n’arrive pas à savoir comment procéder:

 class AppMeta { public int Id { get; set; } } var excludedAppIds = new List {2, 3, 5, 6}; var unfilteredApps = new List { new AppMeta {Id = 1}, new AppMeta {Id = 2}, new AppMeta {Id = 3}, new AppMeta {Id = 4}, new AppMeta {Id = 5} } 

Comment puis-je obtenir une liste d’ AppMeta qui filtre les AppMeta ?

Essayez une simple requête où

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

La méthode except utilise l’égalité, vos listes contiennent des objects de types différents, donc aucun des éléments qu’elles contiennent ne sera égal!

J’utilise une méthode d’extension pour Except, qui vous permet de comparer des pommes avec des oranges dans la mesure où elles ont toutes les deux quelque chose de commun qui peut être utilisé pour les comparer, comme un identifiant ou une clé.

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

puis utilisez-le comme ceci:

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

cette extension est très similaire à la réponse de ColinE, elle est simplement regroupée dans une extension nette qui peut être réutilisée sans trop de charge mentale.

La réponse de ColinE est simple et élégante. Si vos listes sont plus grandes et que la liste des applications exclues est sortingée, BinarySearch peut s’avérer plus rapide que Contains .

EXEMPLE:

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

C’est ce dont LINQ a besoin

 public static IEnumerable Except(this IEnumerable items, IEnumerable other, Func 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; } 

Construisez une List partir de la liste exclue et utilisez l’opérateur Except Linq.

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

J’aime les méthodes d’extension Except, mais la question d’origine ne dispose pas d’un access par clé symésortingque et je préfère utiliser Contient (ou la variante Toute) pour y adhérer. Je tiens donc à remercier la réponse de azuneca :

 public static IEnumerable Except(this IEnumerable items, IEnumerable other, Func getKey) { return from item in items where !other.Contains(getKey(item)) select item; } 

Ce qui peut alors être utilisé comme:

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

De plus, cette version permet d’avoir besoin d’un mappage pour l’exception IEnumerable à l’aide de Select:

 var filteredApps = unfilteredApps.Except(excludedApps.Select(a => a.Id), ua => ua.Id); 
 public static class ExceptByProperty { public static List ExceptBYProperty(this List list, List list2, Expression> propertyLambda) { Type type = typeof(T); MemberExpression member = propertyLambda.Body as MemberExpression; if (member == null) throw new ArgumentException(ssortingng.Format( "Expression '{0}' refers to a method, not a property.", propertyLambda.ToSsortingng())); PropertyInfo propInfo = member.Member as PropertyInfo; if (propInfo == null) throw new ArgumentException(ssortingng.Format( "Expression '{0}' refers to a field, not a property.", propertyLambda.ToSsortingng())); if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType)) throw new ArgumentException(ssortingng.Format( "Expresion '{0}' refers to a property that is not from type {1}.", propertyLambda.ToSsortingng(), type)); Func func = propertyLambda.Comstack(); var ids = list2.Select(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 ssortingng Name { get; set; } } 

Pour tester ceci:

  List a = new List(); List b = new List(); 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(x => x.ID); var items = a.ExceptBYProperty(b, u => u.ID);