LINQ эквивалент F # List.partition - PullRequest
0 голосов
/ 04 ноября 2019

Используя LINQ, как вы разделяете IEnumerable способом, эквивалентным List.parition функции F #? Результатом должна быть исходная последовательность, разделенная на две последовательности на основе предиката, причем предикат оценивается один раз для исходного элемента.

Ответы [ 2 ]

1 голос
/ 09 ноября 2019

Enumerable.ToLookup подходит близко.

var l = new int[]{1,2,3,4,5};

var split = l.ToLookup( i => i > 2);

var a = split[true]; // 3,4,5
var b = split[false]; // 1,2

Это не сложно заключить в функцию.

public static (IEnumerable<TSource> Yeses, IEnumerable<TSource> Nos) Partition<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    //Null handling?
    var s = source.ToLookup(i => predicate(i));
    return (s[true], s[false]);
}

public static void Main(string[] args)
{
    var l = new int[] { 1,2,3,4,5 };

    var s = Partition(l, i => { Console.WriteLine($"Called: {i}"); return i > 2;});

    Console.WriteLine(string.Join(",", s.Yeses));
    Console.WriteLine(string.Join(",", s.Nos));
}
Called: 1
Called: 2
Called: 3
Called: 4
Called: 5
3,4,5
1,2
0 голосов
/ 09 ноября 2019

Я бы предложил такой способ:

F # (тест-кейс)

let list1 = [ 1 .. 10 ]
let listEven, listOdd = List.partition (fun elem -> elem % 2 = 0) list1
printfn "Evens: %A\nOdds: %A" listEven listOdd
/* Result:
Evens: [2; 4; 6; 8; 10]
Odds: [1; 3; 5; 7; 9]
*/

C # (тест-кейс)

var list1 = Enumerable.Range(1, 10);

var (listEven, listOdd) = list1
    .GroupBy(key => key % 2 == 0)
    .Aggregate(
        (part1: Enumerable.Empty<int>(), part2: Enumerable.Empty<int>()),
        (accumulator, value) => {
            if (value.Key)
            {
                accumulator.part1 = value.ToArray();    
            }
            else
            {
                accumulator.part2 = value.ToArray();
            }

            return accumulator;
        });

Console.WriteLine($"Evens: [{string.Join("; ", listEven)}]\nOdds: [{string.Join("; ", listOdd)}]");
/* Result:
Evens: [2; 4; 6; 8; 10]
Odds: [1; 3; 5; 7; 9]
*/

Для повторного использования и обобщения этого метода определите метод extension:

public static class EnumerableExtensions
{
    public static (T[] part1, T[] part2) Partition<T>(this IEnumerable<T> self, Func<T, bool> predicate)
    {
        return self
            .GroupBy(predicate)
            .Aggregate(
                (part1: Array.Empty<T>(), part2: Array.Empty<T>()),
                (accumulator, value) => {
                    if (value.Key)
                    {
                        accumulator.part1 = value.ToArray();
                    }
                    else
                    {
                        accumulator.part2 = value.ToArray();
                    }

                    return accumulator;
                });
    }
}

Используйте его следующим образом:

var list1 = Enumerable.Range(1, 10);
var (listEven, listOdd) = list1.Partition(v => v % 2 == 0);

Console.WriteLine($"Evens: [{string.Join("; ", listEven)}]\nOdds: [{string.Join("; ", listOdd)}]"); 
/* Result:
Evens: [2; 4; 6; 8; 10]
Odds: [1; 3; 5; 7; 9]
*/

var str = "aAbbBcCddD";
var (lowers, uppers) = str.Partition(v => char.IsLower(v));

Console.WriteLine($"Lowers: [{string.Join("; ", lowers)}]\nUppers: [{string.Join("; ", uppers)}]"); 
/* Result:
Lowers: [a; b; b; c; d; d]
Uppers: [A; B; C; D]
*/
...