С# разделить список на два списка, используя функцию bool

Скажем, у меня есть List<string> listOfStrings, и я хочу разделить этот список на два списка на основе некоторого предиката. Например, первый список должен содержать все строки, начинающиеся с буквы, а второй — список строк, которые не начинаются.

Теперь я бы сделал это так:

var firstList = listOfStrings.Where(str => predicate(str));
var secondList = listOfStrings.Where(str => !predicate(str));

Есть ли лучший способ сделать это в одну строку?


person eddyP23    schedule 13.06.2019    source источник


Ответы (4)


Вы можете использовать Linq GroupBy():

var splitted = listOfStrings.GroupBy(s => Char.IsLetter(s[0]));

А с вашим predicate это будет:

Func<string, bool> predicate;

var splitted = listOfStrings.GroupBy(predicate);

Использование:

Самый простой способ — преобразовать сгруппированные данные в Dictionary<bool, IEnumerable<string>>, когда ключом является bool, обозначающий, начинаются ли элементы в нем с буквы:

var splitted = list.GroupBy(x => Char.IsLetter(x[0]))
                   .ToDictionary(x => x.Key, z => z.ToArray());  

var startWithLetter = splitted[true];
var dontStartWithLetter = splitted[false];

Конечно, есть много способов преобразовать данные в желаемую структуру, но, на мой взгляд, вышеизложенное довольно лаконично.

См. MSDN.

person haim770    schedule 13.06.2019
comment
Спасибо, но для полноты, не могли бы вы добавить пример того, как получить оба списка из splitted? - person eddyP23; 13.06.2019
comment
@eddyP23, Готово. - person haim770; 13.06.2019

В Kotlin есть функция partition (исходники). С# версия:

var (first, second) = list.Partition(x => x.IsTrue);

Расширение:

public static (IEnumerable<T> first, IEnumerable<T> second) Partition<T>(this IEnumerable<T> list, Func<T, bool> predicate)
{
    var lookup = list.ToLookup(predicate);
    return (lookup[true], lookup[false]);
}

Может быть удобнее вернуть List<T> или использовать GroupBy или что-то еще, в зависимости от варианта использования.

person thomius    schedule 15.05.2021

Вы можете использовать «GroupBy» или «ToLookup», в зависимости от того, что вы будете делать с результатами. Проверьте также поиск по сравнению с groupby

person Fortega    schedule 13.06.2019

Я бы сделал что-то вроде этого:

class Program
{
    static void Main(string[] args)
    {
        Func<string, bool> startsWithA = s => s[0] == 'a';

        List<string> listOfStrings = new List<string>()
        {
            "abc",
            "acb",
            "bac",
            "bca",
            "cab",
            "cba"
        };

        Dictionary<bool, List<string>> dictionaryOfListsOfStrings = listOfStrings.GroupBy(startsWithA).ToDictionary(x => x.Key, x => x.ToList());
    }
}
person Marco Salerno    schedule 13.06.2019