使用LINQ,如何以与F#的List.parition函数等效的方式对IEnumerable
进行分区?结果应将源序列基于谓词划分为两个序列,每个源项对该谓词进行一次评估。
LINQ等效于F#List.partition
•
问答
sophiezelmani927 回答:LINQ等效于F#List.partition
var l = new int[]{1,2,3,4,5};
var split = l.ToLookup( i => i > 2);
var a = split[true]; // 3,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,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,5
1,2
,
我会这样建议:
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]
*/
要重用和通用化此方法,请定义扩展方法:
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>()),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("; ",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]
*/