Filter Linq BEHALVE op eigenschappen

Dit lijkt misschien dom, maar alle voorbeelden die ik heb gevonden voor het gebruik van Except in linq gebruikt twee lijsten of matrices van alleen tekenreeksen of gehele getallen en filtert ze op basis van de wedstrijden, bijvoorbeeld:

var excludes = users.Except(matches);

Ik wil uitsluiten om mijn code kort en eenvoudig, maar kan niet lijken te vinden van hoe het volgende te doen:

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

Hoe kan ik een lijst krijgen van AppMeta terug dat de filters op excludedAppIds?

InformationsquelleAutor Wesley | 2013-03-21

 

8 Replies
  1. 66

    Probeer een eenvoudige query waar

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

    Behalve de methode maakt gebruik van gelijkheid, uw lijsten bevatten objecten van verschillende typen, zodat geen van de items die ze bevatten, worden gelijk!

    • Voor efficiëntie, stel het opslaan van excludedAppIds als een HashSet, anders heb je een O(N²) algoritme dat verschuift van uw lijst met uitgesloten zo vaak zijn er elementen in de bron.
    • Dit is handig! Kan je daar wat meer over een beetje meer? Wat zou de Grote O-efficiëntie anders? Dus in plaats van het algoritme doorlopen van de lijst met uitsluitingen zo vaak zijn er elementen in de lijst wordt gefilterd, wat gebeurt er dan? En wat gebeurt er als je het filteren van een IQueryable?
    • een hash set maakt gebruik van de hash-code van de elementen om een boom te zoeken. Het maakt dus niet te herhalen met de collectie bij het zoeken naar een match. Het resultaat is O(N * hash diepte) aka O(N)
  2. 13

    Gebruik ik een extension method, Behalve, dat kunt u vergelijken Appels met peren zolang zij beiden hebben iets gemeenschappelijk, dat gebruikt kan worden om ze te vergelijken, zoals een Id-of-Toets.

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

    gebruik het dan iets als dit:

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

    de uitbreiding is zeer vergelijkbaar met ColinE ’s antwoord, het is gewoon verpakt in een nette extensie die hergebruikt kunnen worden zonder al te veel geestelijke overhead.

  3. 13

    ColinE het antwoord is eenvoudig en elegant. Als je lijsten zijn groter en mits de uitgesloten apps lijst is gesorteerd, BinarySearch<T> kan blijken sneller dan Contains.

    VOORBEELD:

    unfilteredApps.Where(i => excludedAppIds.BinarySearch(i.Id) < 0);
    • Dat is heel nuttig, dank je. In dit geval zijn ze niet groot en is het uitgesloten gesorteerde lijst, maar ik zal het bestand met deze verwijderd en verzenden upvotes uw richting.
    • Heel erg bedankt. Ik kom na een voorbeeld voor toekomstige lezers.
    • !(a >= b) is een interessante manier van zeggen a<b 😉
    • Bedankt. Bijgewerkt.
  4. 11

    Dit is wat LINQ behoeften

    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; 
    }
    • Dat zou een geweldige uitbreiding methode
    • En het werkt zo leuk…
    • Dat is prima, behalve dat ik ben niet zeker join is de meest efficiënte manier om uit te sluiten wedstrijden, en dezelfde getKey wordt gebruikt voor beide zijden, die niet zou werken voor de oorspronkelijke vraag.
  5. 7

    De bouw van een List<AppMeta> uit de Lijst uitgesloten en het gebruik van de Uitzondering van Linq operator.

    var ex = excludedAppIds.Select(x => new AppMeta{Id = x}).ToList();                           
    var result = ex.Except(unfilteredApps).ToList();
    • Deze oplossing zorgt voor grote datasets +1
    • Het kost te veel geheugen en tijd.
  6. 1

    Ik graag de Uitzondering van uitbreiding methoden, maar de oorspronkelijke vraag niet symmetrische sleutel en ik heb liever Bevat (of Een variant) uit te nodigen, dus met alle credit naar azuneca antwoord:

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

    Die vervolgens gebruikt kan worden als:

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

    Ook, deze versie biedt voor nodig een mapping voor de uitzondering IEnumerable door een Select:

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

    MoreLinq heeft iets nuttigs voor deze
    MoreLinq.De bron.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);
                }
            }
        }
    }
  8. -4
    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; }
    }

    Voor deze Test:

            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);
    • Reflectie moet altijd uw laatste redmiddel, en dat is een stuk van reflectie voor een eenvoudige opgave was dat niet nodig is.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *