Linq All on empty collection

后端 未结 8 2052
面向向阳花
面向向阳花 2021-01-08 00:24

I need to check if all definitions contains some specific data. It works fine except the case when GroupBy returns empty collection.

var exist = dbContext.De         


        
相关标签:
8条回答
  • 2021-01-08 00:56

    Here is another trick:

    var exist = dbContext.Definitions
        .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId)
        .GroupBy(x => x.PropertyTypeId)
        .Min(some_condition ? (int?)1 : 0) == 1;
    

    It utilizes the fact that the above Min<int?> method returns:

    (A) null when the set is empty
    (B) 0 if the condition is not satisfied for some element
    (C) 1 if the condition is satisfied for all elements

    so we simple check the result for (C) using the nullable value comparison rules.

    0 讨论(0)
  • 2021-01-08 00:58

    What about writing your own extension method? (I am pretty sure you will name it better)

    public static bool NotEmptyAll<T>(
        this IEnumerable<T> collection, 
        Func<T, bool> predicate)
    {
        return collection != null
            && collection.Any()
            && collection.All(predicate);
    }
    

    Then call it instead of All

    var exist = definitions.Where(
            x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId)
             .GroupBy(x => x.PropertyTypeId)
             .NotEmptyAll(
                 ...some condition...));
    
    0 讨论(0)
  • 2021-01-08 01:02

    Edit: first answer wouldn't have worked.

    If you rearrange your query somewhat, you can use DefaultIfEmpty without needing to change your condition:

    var exist = dbContext.Definitions
                         .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) 
                                      && x.CountryId == countryId)
                         .GroupBy(x => x.PropertyTypeId);
    
               // apply the condition to all entries, 
               // resulting in sequence of bools (or empty), 
               // to permit the next step
                         .Select(...some condition...) 
    
               //if seq is empty, add `false`
                         .DefaultIfEmpty(false)
    
               //All with identity function to apply the query and calculate result
                         .All(b => b)
             );
    
    0 讨论(0)
  • 2021-01-08 01:03

    Here's the alternative to All that returns false if collection is empty:

    var collection = Enumerable.Range(0, 0); //empty collection
    
    collection
      .Select(IsValid)
      .DefaultIfEmpty(false)
      .All(b => b);
    

    Or as an extension method:

    public static bool AnyAndAll<T>(IEnumerable<T> collection, Func<T, bool> predicate) =>
      collection
        .Select(predicate)
        .DefaultIfEmpty(false)
        .All(b => b);
    
    0 讨论(0)
  • 2021-01-08 01:11

    If you're using LINQ to Objects, I'd just write my own extension method. My Edulinq project has sample code for All, and adapting that is pretty simple:

    public static bool AnyAndAll<TSource>(
        this IEnumerable<TSource> source,
        Func<TSource, bool> predicate)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }
        if (predicate == null)
        {
            throw new ArgumentNullException(nameof(predicate));
        }
    
        bool any = false;
        foreach (TSource item in source)
        {
            any = true;
            if (!predicate(item))
            {
                return false;
            }
        }
        return any;
    }
    

    This avoids evaluating the input more than once.

    0 讨论(0)
  • 2021-01-08 01:12

    You may be able to do this using an Aggregate, along the lines of:

    .Aggregate(new {exists = 0, matches = 0}, (a, g) =>
            new {exists = a.exists + 1, matches = a.matches + g > 10 ? 1 : 0})
    

    (Here, g > 10 is my test)

    And then simple logic that exists is greater than zero and that exists and matches have the same value.

    This avoids running the whole query twice.

    0 讨论(0)
提交回复
热议问题