leora
leora

Reputation: 196679

What is the best way to trim a list?

I have a List of strings. Its being generated elsewhere but i will generate it below to help describe this simplified example

var list = new List<string>();
list.Add("Joe");
list.Add("");
list.Add("Bill");
list.Add("Bill");
list.Add("");
list.Add("Scott");
list.Add("Joe");
list.Add("");
list.Add("");

list = TrimList(list);

I would like a function that "trims" this list and by trim I want to remove all items at the end of the array that are blank strings (the final two in this case).

NOTE: I still want to keep the blank one that is the second item in the array (or any other one that is just not at the end) so I can't do a .Where(r=> String.isNullOrEmpty(r))

Upvotes: 1

Views: 577

Answers (5)

RX_DID_RX
RX_DID_RX

Reputation: 4183

I always like to come up with the most generic solution possible. Why restrict yourself with lists and strings? Let's make an algorithm for generic enumerable!

public static class EnumerableExtensions
{
    public static IEnumerable<T> TrimEnd<T>(this IEnumerable<T> enumerable, Predicate<T> predicate)
    {
        if (predicate == null)
        {
            throw new ArgumentNullException("predicate");
        }

        var accumulator = new LinkedList<T>();
        foreach (var item in enumerable)
        {
            if (predicate(item))
            {
                accumulator.AddLast(item);
            }
            else
            {
                foreach (var accumulated in accumulator)
                {
                    yield return accumulated;
                }

                accumulator.Clear();

                yield return item;
            }
        }
    }
}

Use it like this:

var list = new[]
{
    "Joe", 
    "", 
    "Bill", 
    "Bill", 
    "", 
    "Scott", 
    "Joe", 
    "", 
    ""
};
foreach (var item in list.TrimEnd(string.IsNullOrEmpty))
{
    Console.WriteLine(item);
}

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1502196

I would just write it without any LINQ, to be honest- after all, you're modifying a collection rather than just querying it:

void TrimList(List<string> list)
{
    int lastNonEmpty = list.FindLastIndex(x => !string.IsNullOrEmpty(x));
    int firstToRemove = lastNonEmpty + 1;
    list.RemoveRange(firstToRemove, list.Count - firstToRemove);
}

If you actually want to create a new list, then the LINQ-based solutions are okay... although potentially somewhat inefficient (as Reverse has to buffer everything).

Upvotes: 10

BradleyDotNET
BradleyDotNET

Reputation: 61369

Take advantage of Reverse and SkipWhile.

list = list.Reverse().SkipWhile(s => String.IsNullOrEmpty(s)).Reverse().ToList();

Upvotes: 3

Simon Whitehead
Simon Whitehead

Reputation: 65077

List<T> (not the interface) has a FindLastIndex method. Therefore you can wrap that in a method:

static IList<string> TrimList(List<string> input) {
    return input.Take(input.FindLastIndex(x => !string.IsNullOrEmpty(x)) + 1)
        .ToList();
}

This produces a copy, whereas Jon's modifies the list.

Upvotes: 1

KC-NH
KC-NH

Reputation: 748

The only solution I can think of is to code a loop that starts at the end of the list and searches for an element that is not an empty string. Don't know of any library functions that would help. Once you know the last good element, you know which ones to remove.

Be careful not to modify the collection while you are iterating over it. Tends to break the iterator.

Upvotes: 0

Related Questions