My specific requirement is that I have an IEnumerable<IEnumerable<string>>
, and I want to "take" all items in the outer enumeration except any "empty" trailing items, where "empty" means all strings are null/empty or the inner enumeration is empty. Note that I want to keep any empty items that appear before the last non-empty one. For example:
Item 1: a, b, c
Item 2: (nothing)
Item 3: a, f, g, h
Item 4: (nothing)
Item 5: (nothing)
I would like to keep items 1–3 but trim items 4 and 5.
In a more general sense, I have an enumeration of items, where I want to trim any trailing items that satisfy a condition, that appear behind the last item that does not satisfy the condition.
For the sake of choosing an appropriate solution, I might add that the outer enumeration will usually contain a few hundred up to a few hundred thousand items, while the inner enumerations contain only a few items each. There will probably be only a couple of empty items that I need to trim.
My current solution puts all outer items in a list (after transforming them with .Select(...)
), and then in a loop keep removing the last item if it's empty, until a non-empty item is found.
There is no standard efficient LINQ solution. I would go with a custom extension "LINQ like" method like this:
public static class EnumerableExtensions
{
public static IEnumerable<T> SkipLastWhile<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
var skipBuffer = new List<T>();
foreach (var item in source)
{
if (predicate(item))
skipBuffer.Add(item);
else
{
if (skipBuffer.Count > 0)
{
foreach (var skipped in skipBuffer)
yield return skipped;
skipBuffer.Clear();
}
yield return item;
}
}
}
}
It requires additional space for buffering the longest item sequence satisfying the skip predicate while the LINQ Reverse
method has to buffer the whole input sequence.
The usage will be:
var result = input.SkipLastWhile(e => !e.Any());
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