Can I split an IEnumerable into two by a boolean criteria without two queries?

前端 未结 3 827
再見小時候
再見小時候 2020-12-01 02:19

Can I split an IEnumerable into two IEnumerable using LINQ and only a single query/LINQ statement?

I want to avoid iter

相关标签:
3条回答
  • 2020-12-01 03:05

    Some people like Dictionaries, but I prefer Lookups due to the behavior when a key is missing.

    IEnumerable<MyObj> allValues = ...
    ILookup<bool, MyObj> theLookup = allValues.ToLookup(val => val.SomeProp);
    
      //does not throw when there are not any true elements.
    List<MyObj> trues = theLookup[true].ToList();
      //does not throw when there are not any false elements.
    List<MyObj> falses = theLookup[false].ToList();
    

    Unfortunately, this approach enumerates twice - once to create the lookup, then once to create the lists.

    If you don't really need lists, you can get this down to a single iteration:

    IEnumerable<MyObj> trues = theLookup[true];
    IEnumerable<MyObj> falses = theLookup[false];
    
    0 讨论(0)
  • 2020-12-01 03:10

    Copy pasta extension method for your convenience.

    public static void Fork<T>(
        this IEnumerable<T> source,
        Func<T, bool> pred,
        out IEnumerable<T> matches,
        out IEnumerable<T> nonMatches)
    {
        var groupedByMatching = source.ToLookup(pred);
        matches = groupedByMatching[true];
        nonMatches = groupedByMatching[false];
    }
    

    Or using tuples in C# 7.0

    public static (IEnumerable<T> matches, IEnumerable<T> nonMatches) Fork<T>(
        this IEnumerable<T> source,
        Func<T, bool> pred)
    {
        var groupedByMatching = source.ToLookup(pred);
        return (groupedByMatching[true], groupedByMatching[false]);
    }
    
    // Ex.
    var numbers = new [] { 1, 2, 3, 4, 5, 6, 7, 8 };
    var (numbersLessThanEqualFour, numbersMoreThanFour) = numbers.Fork(x => x <= 4);
    
    0 讨论(0)
  • 2020-12-01 03:14

    You can use this:

    var groups = allValues.GroupBy(val => val.SomeProp);
    

    To force immediate evaluation like in your example:

    var groups = allValues.GroupBy(val => val.SomeProp)
                          .ToDictionary(g => g.Key, g => g.ToList());
    List<MyObj> trues = groups[true];
    List<MyObj> falses = groups[false];
    
    0 讨论(0)
提交回复
热议问题