我喜欢一种方法,可以在谓词中分割IEnumerable
,通过相对于谓词的索引将项目分组在一起。例如,它可以在满足List<string>
的项目中分割x => MyRegex.Match(x).Success
,在&#34;之间使用项目&#34;这些比赛被归为一类。
它的签名看起来像行
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate,
int bool count
)
,可能包含一个包含所有分隔符的输出的额外元素。
实现此方法是否比foreach
循环更有效和/或更紧凑?我觉得应该可以用LINQ方法实现,但我不能指责它。
示例:
string[] arr = {"One", "Two", "Three", "Nine", "Four", "Seven", "Five"};
arr.Split(x => x.EndsWith("e"));
以下任何一种情况都可以:
IEnumerable<string> {{}, {"Two"}, {}, {"Four", "Seven"}, {}}
IEnumerable<string> {{"Two"}, {"Four", "Seven"}}
用于存储匹配项的可选元素为{"One", "Three", "Nine", "Five"}
。
答案 0 :(得分:19)
如果您希望避免使用扩展方法,则可以始终使用:
var arr = new[] {"One", "Two", "Three", "Nine", "Four", "Seven", "Five"};
var result = arr.ToLookup(x => x.EndsWith("e"));
// result[true] == One Three Nine Five
// result[false] == Two Four Seven
答案 1 :(得分:4)
您应该通过扩展方法执行此操作(此方法假定您忽略分区项目):
/// <summary>Splits an enumeration based on a predicate.</summary>
/// <remarks>
/// This method drops partitioning elements.
/// </remarks>
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> partitionBy,
bool removeEmptyEntries = false,
int count = -1)
{
int yielded = 0;
var items = new List<TSource>();
foreach (var item in source)
{
if (!partitionBy(item))
items.Add(item);
else if (!removeEmptyEntries || items.Count > 0)
{
yield return items.ToArray();
items.Clear();
if (count > 0 && ++yielded == count) yield break;
}
}
if (items.Count > 0) yield return items.ToArray();
}
答案 2 :(得分:3)
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
List<TSource> group = new List<TSource>();
foreach (TSource item in source)
{
if (predicate(item))
{
yield return group.AsEnumerable();
group = new List<TSource>();
}
else
{
group.Add(item);
}
}
yield return group.AsEnumerable();
}
答案 3 :(得分:2)
public static IEnumerable<IEnumerable<TSource>> Partition<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
yield return source.Where(predicate);
yield return source.Where(x => !predicate(x));
}
示例:
var list = new List<int> { 1, 2, 3, 4, 5 };
var parts = list.Partition(x => x % 2 == 0);
var even = parts.ElementAt(0); // contains 2, 4
var odd = parts.ElementAt(1); // contains 1, 3, 5
答案 4 :(得分:1)
我将使用提供的键选择器对源集合进行分区。这样,您还可以根据简单属性对复杂对象进行切片。
public static class LinqExtension
{
public static IEnumerable<IEnumerable<TSource>> Slice<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> selector,
Func<TKey, TKey, bool> partition)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (selector == null) throw new ArgumentNullException(nameof(selector));
if (partition == null) throw new ArgumentNullException(nameof(partition));
var seed = new List<List<TSource>> { new List<TSource>() };
return source.Aggregate(seed, (slices, current) =>
{
var slice = slices.Last();
if (slice.Any())
{
var previous = slice.Last();
if (partition(selector(previous), selector(current)))
{
slice = new List<TSource>();
slices.Add(slice);
}
}
slice.Add(current);
return slices;
}).Select(x => x.AsReadOnly());
}
}
一个简单的例子:
// slice when the difference between two adjacent elements is bigger than 5
var input = new[] { 1, 2, 3, 10, 11, 20 };
var output = input.Slice(i => i, (x, y) => y - x > 5);