我可以通过布尔条件将IEnumerable拆分为两个而不需要两个查询吗?

SFu*_*n28 65 .net linq

我可以使用LINQ和只有一个查询/ LINQ语句将其拆分IEnumerable<T>为两个IEnumerable<T>吗?

我想避免迭代IEnumerable<T>两次.例如,是否可以组合下面的最后两个语句,以便只遍历一次allValues?

IEnumerable<MyObj> allValues = ...
List<MyObj> trues = allValues.Where( val => val.SomeProp ).ToList();
List<MyObj> falses = allValues.Where( val => !val.SomeProp ).ToList();
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 66

你可以用这个:

var groups = allValues.GroupBy(val => val.SomeProp);
Run Code Online (Sandbox Code Playgroud)

要强制立即评估,例如在您的示例中:

var groups = allValues.GroupBy(val => val.SomeProp)
                      .ToDictionary(g => g.Key, g => g.ToList());
List<MyObj> trues = groups[true];
List<MyObj> falses = groups[false];
Run Code Online (Sandbox Code Playgroud)

  • 如果从未使用过一个密钥(true或false),则会崩溃.例如,如果SomeProp始终为true,则组[false]将因异常而崩溃"字典中不存在给定的键" (5认同)
  • GroupBy 使用排序来分组。我不知道坚持扫描迭代两次是否比进行某种扫描更好。 (4认同)

Amy*_*y B 61

有些人喜欢字典,但我更喜欢Lookups,因为缺少密钥时的行为.

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();
Run Code Online (Sandbox Code Playgroud)

不幸的是,这种方法枚举了两次 - 一次创建查找,然后一次创建列表.

如果你真的不需要列表,你可以将它归结为一次迭代:

IEnumerable<MyObj> trues = theLookup[true];
IEnumerable<MyObj> falses = theLookup[false];
Run Code Online (Sandbox Code Playgroud)

  • +1用于建议查找以及在使用Dictionary时注意到丢失键的潜在问题. (5认同)

Mic*_*Fry 10

为方便起见,复制面食延长方法.

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];
}
Run Code Online (Sandbox Code Playgroud)

或者在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);
Run Code Online (Sandbox Code Playgroud)

  • 在此处添加注释以说c ++中的等效项是std :: partiton。可能会使其他人更容易找到C#分区功能。 (2认同)
  • 阅读此答案后,我可以在词典中添加短语“ Copy Pasta”。 (2认同)