如何使用linq从列表中删除尾随对象?

4

我有一个包含属性的对象集合,我想在LINQ中删除所有以(例如)0 为值的尾随对象。

public class Object
{
    public Object(){}

    public int Property {get; set;}
}

如果我有一个对象列表:

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"

我该如何去除此列表中的“尾随零”?我不想删除所有属性为零的项,但如果后面没有更大的属性值跟随,则要删除列表中任何具有零属性值的对象。

1
你尝试了什么?你遇到了什么问题?为什么需要使用LINQ?你经常使用LINQ来修改现有的集合吗?你知道你的对象列表所在的容器类是什么类型,或者像你的问题中建议的那样,你只是调用一堆构造函数并将实例扔到风中,就像海德公园里的蝴蝶一样? - 15ee8f99-57ff-4f92-890c-b56153
希望你原始代码中没有把这个类命名为Object - user47589
虽然不是很好,但是可以使用var result = list.Reverse().SkipWhile(x => x.Property == 0).Reverse();这个代码。 - juharr
4
所有类应该命名为Object,所有属性应该命名为Property;这本质上是自我说明的,并且强制执行“一次只做一件事”的规则。 - 15ee8f99-57ff-4f92-890c-b56153
1
@EdPlunkett 哈哈,我以为人们会通过所有实例被神奇地抛到风中来理解我的意思。如果这是我的实际代码,显然我会问其他问题。 - leigero
显示剩余5条评论
4个回答

8

针对有限大小序列的标准解决方案 - 反转、从开头删除、反转:

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

这种方法非常不优化,如果你有一个真正的集合(例如List),最好只删除从最后一个索引开始的项目。


4
编写一个扩展方法:
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;
            }
        }
    }
}

如果您有一个名为lIEnumerable<Object>,则可以使用以下方式调用TrimTrailing

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

不过要注意,最坏情况下,它会缓存所有来自items的项目然后丢弃缓存。


我喜欢这种方式,因为它比反转+跳过+反转更易读。 - TruthOf42

3

只需反向迭代您的列表,删除任何0条目,并停止在列表的开头或Property != 0处。

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

这将允许您对列表进行单次遍历。

1
为什么不直接使用if (list[i].Property != 0) break;list.RemoveAt(i);呢? - juharr
@juharr 确定 - 这是任何合理的重构。 - Daniel A. White
1
有时候,将其反转以使其更适合使用情况可能是最好的选择。 - Daniel A. White
感觉代码有点多,而且 i > 0 稍微有点不对。for (int i = list.Count - 1; i >=0 && list[i].Property == 0; i--) list.RemoveAt(i); - Slai

1
你可以使用FindLastIndex来查找最后一个非0索引,然后Take该元素。
var result = list.Take(list.FindLastIndex(x => x.Property != 0) + 1);

这只会返回示例中的第一个项目。 - juharr
@juharr:没错,我现在明白了。谢谢。已更新回答。 - Arturo Menchaca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接