Могу ли я разделить IEnumerable на два по булевому критерию без двух запросов? - PullRequest
62 голосов
/ 28 декабря 2010

Можно ли разделить IEnumerable<T> на два IEnumerable<T>, используя LINQ и только один оператор запроса / LINQ?

Я хочу избежать повторения IEnumerable<T> дважды. Например, возможно ли объединить последние два оператора ниже, чтобы allValues ​​проходился только один раз?

IEnumerable<MyObj> allValues = ...
List<MyObj> trues = allValues.Where( val => val.SomeProp ).ToList();
List<MyObj> falses = allValues.Where( val => !val.SomeProp ).ToList();

Ответы [ 3 ]

63 голосов
/ 28 декабря 2010

Вы можете использовать это:

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

Чтобы вызвать немедленную оценку, как в вашем примере:

var groups = allValues.GroupBy(val => val.SomeProp)
                      .ToDictionary(g => g.Key, g => g.ToList());
List<MyObj> trues = groups[true];
List<MyObj> falses = groups[false];
61 голосов
/ 29 декабря 2010

Некоторым людям нравятся словари, но я предпочитаю поиск из-за поведения, когда ключ отсутствует.

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

К сожалению, этот подход перечисляет дважды - один раз для создания поиска, затем один раз для создания списков.

Если вам не нужны списки, вы можете сделать это за одну итерацию:

IEnumerable<MyObj> trues = theLookup[true];
IEnumerable<MyObj> falses = theLookup[false];
8 голосов
/ 03 мая 2017

Скопируйте метод расширения пасты для вашего удобства.

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

Или используйте кортежи в 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...