I'd like a method that would split an IEnumerable
at a predicate, grouping items together by their index relative to the predicate. For example, it could split a List<string>
at items satisfying x => MyRegex.Match(x).Success
, with items "in between" such matches being grouped together.
Its signature could look something line
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate,
int bool count
)
, possibly with an extra element of the output containing all of the dividers.
Is there a more efficient and/or compact way to implement this than a foreach
loop? I feel like it should be possible to implement with LINQ methods, but I can't put my finger on it.
Example:
string[] arr = {"One", "Two", "Three", "Nine", "Four", "Seven", "Five"};
arr.Split(x => x.EndsWith("e"));
Either of the following would be OK:
IEnumerable<string> {{}, {"Two"}, {}, {"Four", "Seven"}, {}}
IEnumerable<string> {{"Two"}, {"Four", "Seven"}}
the optional element for storing matches would be {"One", "Three", "Nine", "Five"}
.
If you're looking to avoid the extension method, you could always use:
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
You should do this through an extension method (this method assumes you ignore the partitioned item):
/// <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();
}
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();
}
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));
}
Example:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With