Как разделить лист на два по предикату?

141
11 декабря 2019, 09:30

Имеется лист, из которого необходимо получить два согласно предикату. На данный момент делаю так:

var trueItems = new List<string>();
var falseItems = new List<string>();
var list = new List<string> {
    "one", "two", "three"
};
list.ForEach(x => {
    if(x.EndsWith("e")) {
        trueItems.Add(x);
    }
    else {
        falseItems.Add(x);
    }
});

Я думаю, что скорее всего имеется лучший способ, чтоб разделить лист на два согласно условию/предикату. Можно ли реализовать это стандартными средствами LINQ? Можно ли реализовать обобщенное расширение (по типу Where или Select в LINQ), которое бы принимало предикат и возвращало кортеж?

// хотелось бы так...
list.Split(x => x == null).Select((trueItems, falseItems) => { /* ... */});
Answer 1

Благодаря @tym32167, @АндрейNOP и @Monk, авторам всех комментариев и вопросу на enSO пришёл к пониманию следующего:

  • нет ничего страшного в использовании цикла, а при необходимости иметь в результате именно списки, то это вообще единственно возможный вариант с одним проходом;
  • для решения задачи можно использовать Enumerable.GroupBy и Lookup;
var list = new List<string> { "one", "two", "three" };
ILookup<bool, string> res = list.ToLookup(x => x.EndsWith("e"));
// IEnumerable<string> res[true]
// или так, но с дополнительным проходом
// List<string> nonMatches = res[false].ToList(); 
// Метод расширения должен быть определен в неуниверсальном статическом классе
public static (IEnumerable<T> matches, IEnumerable<T> nonMatches) SplitByPredicate<T>(
    this IEnumerable<T> source,
    Func<T, bool> keySelector) {
        var grp = source.ToLookup(keySelector);
        return (grp[true], grp[false]);
    }
var (matches, nonMatches) = list.SpliByPredicate(x => x.EndsWith("e"));

Ссылки по теме

  • ILookup<TKey,TElement> Interface
  • Lookup<TKey,TElement> Class
  • Именованные и неименованные кортежи
Answer 2
list.GroupBy(x => x == null)

а дальше у вас две группы, ключ которых - true или false, обрабатывайте каждую группу как хотите.

Как этим потом пользоваться:

list.GroupBy(x => x == null).SelectMany(g => g.Key ? g.Select(x => ...) : g.Select(x => x...))

После операции группировки работаем мы уже с группами, у которых есть ключи. В данном случае это будет две группы данных, у одной ключ true, у другой false. Можно их после какой то обработки сложить обратно в одну коллекцию через SelectMany.

READ ALSO
Чем интерфейс IEnumerable&lt;T&gt; отличается от IQueryable&lt;T&gt;

Чем интерфейс IEnumerable<T> отличается от IQueryable<T>

Собственно не могу понять основное отличие и для чего они были добавлены

103
Русские символы pdfparser

Русские символы pdfparser

Ссылка на тестовый PDF: https://meganz/#!6yRE1CSC!VvZ7vimkVLk4JDVLU9KrtEeZW9jQeKJn98BStl7JN3s

136
Как создать сортировку товара на сайте со стороны пользователя (wordpress)?

Как создать сортировку товара на сайте со стороны пользователя (wordpress)?

Задача сделать на сайте страницу-каталог где у пользователя будет возможность сортировать товар по фильтрамСайт будет разрабатываться...

149