C# 过滤 Linq 除了属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15540891/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Filter Linq EXCEPT on properties
提问by Wesley
This may seem silly, but all the examples I've found for using Except
in linq use two lists or arrays of only strings or integers and filters them based on the matches, for example:
这可能看起来很傻,但是我发现Except
在 linq 中使用的所有示例都使用两个仅包含字符串或整数的列表或数组,并根据匹配项过滤它们,例如:
var excludes = users.Except(matches);
I want to use exclude to keep my code short and simple, but can't seem to find out how to do the following:
我想使用 exclude 来保持我的代码简短,但似乎无法找到如何执行以下操作:
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}
}
How do I get a list of AppMeta
back that filters on excludedAppIds
?
如何获得AppMeta
过滤后的列表excludedAppIds
?
采纳答案by ColinE
Try a simple where query
尝试一个简单的 where 查询
var filtered = unfilteredApps.Where(i => !excludedAppIds.Contains(i.Id));
The except method uses equality, your lists contain objects of different types, so none of the items they contain will be equal!
except 方法使用相等性,您的列表包含不同类型的对象,因此它们包含的所有项目都不相等!
回答by scartag
Construct a List<AppMeta>
from the excluded List and use the Except Linq operator.
List<AppMeta>
从排除列表构造 a并使用Except Linq 运算符。
var ex = excludedAppIds.Select(x => new AppMeta{Id = x}).ToList();
var result = ex.Except(unfilteredApps).ToList();
回答by dotNET
ColinE's answer is simple and elegant. If your lists are larger and provided that the excluded apps list is sorted, BinarySearch<T>
may prove faster than Contains
.
ColinE 的回答简单而优雅。如果您的列表更大,并且排除的应用程序列表已排序,则BinarySearch<T>
可能比Contains
.
EXAMPLE:
例子:
unfilteredApps.Where(i => excludedAppIds.BinarySearch(i.Id) < 0);
回答by Scott
I use an extension method for Except, that allows you to compare Apples with Oranges as long as they both have something common that can be used to compare them, like an Id or Key.
我使用了一个扩展方法 except,它允许你比较苹果和橙子,只要它们都有一些可以用来比较它们的共同点,比如 Id 或 Key。
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));
}
}
then use it something like this:
然后像这样使用它:
var filteredApps = unfilteredApps.Except(excludedAppIds, a => a.Id, b => b);
the extension is very similar to ColinE 's answer, it's just packaged up into a neat extension that can be reused without to much mental overhead.
该扩展与 ColinE 的答案非常相似,它只是打包成一个简洁的扩展,可以重用而无需太多精神开销。
回答by azuneca
This is what LINQ needs
这就是 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;
}
回答by Ali Yousefi
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; }
}
For Test this:
为了测试这个:
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);
回答by NetMage
I like the Except extension methods, but the original question doesn't have symmetric key access and I prefer Contains (or the Any variation) to join, so with all credit to azuneca's answer:
我喜欢 except 扩展方法,但原始问题没有对称密钥访问,我更喜欢包含(或任何变体)加入,因此所有功劳都归功于azuneca 的回答:
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;
}
Which can then be used like:
然后可以像这样使用:
var filteredApps = unfilteredApps.Except(excludedAppIds, ua => ua.Id);
Also, this version allows for needing a mapping for the exception IEnumerable by using a Select:
此外,此版本允许需要使用 Select 来映射异常 IEnumerable:
var filteredApps = unfilteredApps.Except(excludedApps.Select(a => a.Id), ua => ua.Id);
回答by Adam
MoreLinq has something useful for this MoreLinq.Source.MoreEnumerable.ExceptBy
MoreLinq 对此有一些有用的东西 MoreLinq.Source.MoreEnumerable.ExceptBy
https://github.com/gsscoder/morelinq/blob/master/MoreLinq/ExceptBy.cs
https://github.com/gsscoder/morelinq/blob/master/MoreLinq/ExceptBy.cs
namespace MoreLinq
{
using System;
using System.Collections.Generic;
using System.Linq;
static partial class MoreEnumerable
{
/// <summary>
/// Returns the set of elements in the first sequence which aren't
/// in the second sequence, according to a given key selector.
/// </summary>
/// <remarks>
/// This is a set operation; if multiple elements in <paramref name="first"/> have
/// equal keys, only the first such element is returned.
/// This operator uses deferred execution and streams the results, although
/// a set of keys from <paramref name="second"/> is immediately selected and retained.
/// </remarks>
/// <typeparam name="TSource">The type of the elements in the input sequences.</typeparam>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="first">The sequence of potentially included elements.</param>
/// <param name="second">The sequence of elements whose keys may prevent elements in
/// <paramref name="first"/> from being returned.</param>
/// <param name="keySelector">The mapping from source element to key.</param>
/// <returns>A sequence of elements from <paramref name="first"/> whose key was not also a key for
/// any element in <paramref name="second"/>.</returns>
public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> first,
IEnumerable<TSource> second,
Func<TSource, TKey> keySelector)
{
return ExceptBy(first, second, keySelector, null);
}
/// <summary>
/// Returns the set of elements in the first sequence which aren't
/// in the second sequence, according to a given key selector.
/// </summary>
/// <remarks>
/// This is a set operation; if multiple elements in <paramref name="first"/> have
/// equal keys, only the first such element is returned.
/// This operator uses deferred execution and streams the results, although
/// a set of keys from <paramref name="second"/> is immediately selected and retained.
/// </remarks>
/// <typeparam name="TSource">The type of the elements in the input sequences.</typeparam>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="first">The sequence of potentially included elements.</param>
/// <param name="second">The sequence of elements whose keys may prevent elements in
/// <paramref name="first"/> from being returned.</param>
/// <param name="keySelector">The mapping from source element to key.</param>
/// <param name="keyComparer">The equality comparer to use to determine whether or not keys are equal.
/// If null, the default equality comparer for <c>TSource</c> is used.</param>
/// <returns>A sequence of elements from <paramref name="first"/> whose key was not also a key for
/// any element in <paramref name="second"/>.</returns>
public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> first,
IEnumerable<TSource> second,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> keyComparer)
{
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
if (keySelector == null) throw new ArgumentNullException("keySelector");
return ExceptByImpl(first, second, keySelector, keyComparer);
}
private static IEnumerable<TSource> ExceptByImpl<TSource, TKey>(this IEnumerable<TSource> first,
IEnumerable<TSource> second,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> keyComparer)
{
var keys = new HashSet<TKey>(second.Select(keySelector), keyComparer);
foreach (var element in first)
{
var key = keySelector(element);
if (keys.Contains(key))
{
continue;
}
yield return element;
keys.Add(key);
}
}
}
}