leigero
leigero

Reputation: 3283

How do you remove trailing objects from a list with linq?

I have a collection of objects with properties and I want to remove all the trailing objects with (say) a value of 0 in LINQ.

public class Object
{
    public Object(){}

    public int Property {get; set;}
}

and if I have a list of objects:

new Object(){ Property = 1};
new Object(){ Property = 0};
new Object(){ Property = 9};
new Object(){ Property = 7};
new Object(){ Property = 0}; // "trailing zero"
new Object(){ Property = 0}; // "trailing zero"
new Object(){ Property = 0}; // "trailing zero"

How would I go about removing the "trailing zeros" in this list? I don't want to remove all properties with a zero, but I want to remove any objects from the list with a property value of zero if it it is not later followed by a property value of something greater.

Upvotes: 4

Views: 938

Answers (4)

Jacob Krall
Jacob Krall

Reputation: 28835

Write an extension method:

static class Extensions
{
    public static IEnumerable<T> TrimTrailing<T>(this IEnumerable<T> items,
                                                 Predicate<T> test)
    {

        if (items == null) throw new ArgumentNullException(nameof(items));
        if (test == null) throw new ArgumentNullException(nameof(test));

        var buf = new List<T>();
        foreach (T item in items)
        {
            if (test(item))
            {
                buf.Add(item);
            }
            else
            {
                foreach (T bufferedItem in buf)
                {
                    yield return bufferedItem;
                }
                buf.Clear();
                yield return item;
            }
        }
    }
}

Then, if you have an IEnumerable<Object> called l, you would call TrimTrailing using

var trimmed = l.TrimTrailing(o => o.Property == 0);

Be careful with this, though: in the worst case, it buffers all the items from items and then throws away the buffer.

Upvotes: 4

Daniel A. White
Daniel A. White

Reputation: 190976

Just iterate your list backwards removing any 0 entries and stop either at the beginning of the list or the Property != 0.

for (int i = list.Count - 1; i >= 0; i--)
{
    var item = list[i];
    if (item.Property == 0)
    {
        list.RemoveAt(i);
    }
    else
    {
        break;
    }
}

This will allow for a single pass through your list.

Upvotes: 3

Arturo Menchaca
Arturo Menchaca

Reputation: 15982

You can use FindLastIndex to find the last non-0 index, and Take the elements up to that.

var result = list.Take(list.FindLastIndex(x => x.Property != 0) + 1);

Upvotes: 1

Alexei Levenkov
Alexei Levenkov

Reputation: 100545

Standard solution for sequences of finite size - reverse, remove from start, reverse:

   var withoutTail = sequence
       .Reverse()
       .SkipWhile( x => x == 0) // whatever condition you need
       .Reverse();

This is very non-optimal, so if you actually have real collection (i.e. List) it would be better to just remove items starting from last index.

Upvotes: 8

Related Questions