过滤Linq EXCEPT属性

时间:2013-03-21 06:31:15

标签: c# linq

这可能看起来很愚蠢,但我发现在linq中使用Except的所有示例都使用两个列表或仅包含字符串或整数的数组,并根据匹配过滤它们,例如:

var excludes = users.Except(matches);

我想使用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}
                         }

如何获取AppMeta上的excludedAppIds列表?{/ 1}}

8 个答案:

答案 0 :(得分:60)

尝试简单的查询

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

except方法使用相等,您的列表包含不同类型的对象,因此它们包含的项目都不相同!

答案 1 :(得分:13)

ColinE的答案简单而优雅。如果您的列表较大,并且排除了排除的应用列表,则BinarySearch<T>可能会比Contains更快。

示例:

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

答案 2 :(得分:13)

我使用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));
    }
}

然后使用这样的东西:

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

该扩展与ColinE的答案非常相似,它只是打包成一个整齐的扩展,可以重复使用,而不需要太多的精神开销。

答案 3 :(得分: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; 
}

答案 4 :(得分:7)

从排除的列表构造List<AppMeta>并使用Except Linq运算符。

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

答案 5 :(得分:1)

我喜欢Except扩展方法,但原始问题没有对称密钥访问权限,我更喜欢Contains(或Any变体)加入,因此所有归功于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);

此外,此版本允许使用Select:

来映射异常IEnumerable
var filteredApps = unfilteredApps.Except(excludedApps.Select(a => a.Id), ua => ua.Id);

答案 6 :(得分:0)

MoreLinq对此有帮助 MoreLinq.Source.MoreEnumerable.ExceptBy

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);
            }
        }
    }
}

答案 7 :(得分:-4)

public class MainActivity extends ListActivity{

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MyList.getInstance().getReminders();
    list();
    startService(new Intent(getApplicationContext(), locser.class));
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuItem menu1 = menu.add(0, 1, 0, "Start Service");
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == 1) {
        startService(new Intent(getApplicationContext(), locser.class));
    }
    return super.onOptionsItemSelected(item);
}
void list()
{
    setListAdapter(new listadapter(this, R.layout.reminder_list_layout, MyList.getInstance().getReminders()));
    reminders rem1=new reminders();
    rem1.address="hello";
    rem1.name="je";
    MyList.getInstance().getReminders().add(rem1);
}

为了测试这个:

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; }
}