Given a List<string>
I need to remove all the empty lines at the beginning and at the end of the list.
NOTE: I consider an empty line a line that has no content, but may contain whitespaces and tabs. This is the method to check if a line is empty:
private bool HasContent(string line)
{
if (string.IsNullOrEmpty(line))
return false;
foreach (char c in line)
{
if (c != ' ' && c != '\t')
return true;
}
return false;
}
What efficient and readable code would you suggest to do that?
[" ", " ", " ", " ", "A", "B", " ", "C", " ", "D", " ", " ", " "]
Such a list should be trimmed, by removing all empty lines at the beginning and the end to the following result:
["A", "B", " ", "C", " ", "D"]
var results = textLines1.SkipWhile(e => !HasContent(e))
.Reverse()
.SkipWhile(e => !HasContent(e))
.Reverse()
.ToList();
How it works? Skips all empty lines at from of the list, reverse it and do the same (what actually skips all empty lines from back of the list). After another reverse you're getting proper results in right order.
If list is really huge you can consider using standard while
loops and list indexing because of performance considerations, but for normal data impart of reversion shouldn't really matter.
First off, there is a built in method that checks this: String.IsNullOrWhiteSpace();
The answer ColinE provides does not meet the requirement since it removes all lines that are empty, not just at the beginning or end.
I think you need to build your own solution:
int start = 0, end = sourceList.Count - 1;
while (start < end && String.IsNullOrWhiteSpace(sourceList[start])) start++;
while (end >= start && String.IsNullOrWhiteSpace(sourceList[end])) end--;
return sourceList.Skip(start).Take(end - start + 1);
Using Linq, you could perform the following:
IEnumerable<string> list = sourceList.SkipWhile(source => !HasContent(source))
.TakeWhile(source => HasContent(source));
This 'skips' strings until it finds one with content, then 'takes' all the strings until one without content is found.
Although as @MarcinJuraszek points out, this will stop after the first line that does not have content, rather than removing those at the end of the list.
To do that, you could use the following:
IEnumerable<string> list = sourceList.SkipWhile(source => !HasContent(source))
.Reverse()
.SkipWhile(source => !HasContent(source))
.Reverse();
A little bit convoluted, but should do the trick.
Check this extension method I did just now:
public static class ListExtensions
{
public static List<string> TrimList(this List<string> list)
{
int listCount = list.Count;
List<string> listCopy = list.ToList();
List<string> result = list.ToList();
// This will encapsulate removing an item and the condition to remove it.
// If it removes the whole item at some index, it return TRUE.
Func<int, bool> RemoveItemAt = index =>
{
bool removed = false;
if (string.IsNullOrEmpty(listCopy[index]) || string.IsNullOrWhiteSpace(listCopy[index]))
{
result.Remove(result.First(item => item == listCopy[index]));
removed = true;
}
return removed;
};
// This will encapsulate the iteration over the list and the search of
// empty strings in the given list
Action RemoveWhiteSpaceItems = () =>
{
int listIndex = 0;
while (listIndex < listCount && RemoveItemAt(listIndex))
{
listIndex++;
}
};
// Removing the empty lines at the beginning of the list
RemoveWhiteSpaceItems();
// Now reversing the list in order to remove the
// empty lines at the end of the given list
listCopy.Reverse();
result.Reverse();
// Removing the empty lines at the end of the list
RemoveWhiteSpaceItems();
// Reversing again in order to recover the right list order.
result.Reverse();
return result;
}
}
...and its usage:
List<string> list = new List<string> { "\t", " ", " ", "a", "b", "\t", " ", " " };
// The TrimList() extension method will return a new list without
// the empty items at the beginning and the end of the sample list!
List<string> trimmedList = list.TrimList();
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