【问题标题】:How do you remove trailing objects from a list with linq?如何使用 linq 从列表中删除尾随对象?
【发布时间】:2017-05-21 03:20:47
【问题描述】:

我有一组具有属性的对象,我想在 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"

我将如何删除此列表中的“尾随零”?我不想删除所有为零的属性,但我想从列表中删除任何属性值为 0 的对象,前提是它后面不是更大的属性值。

【问题讨论】:

  • 你有什么样的收藏?
  • @DanielA.White 我目前正在使用 List,但如果它能让我的生活更轻松,我可以更改它。
  • 你尝试了什么?你在哪里遇到麻烦了?为什么需要使用 LINQ?您是否经常使用 LINQ 来更改现有集合?您是否知道您的对象列表在哪种容器类中,或者如您的问题中所建议的那样,您是否只是调用一堆构造函数并将实例扔到风中,就像海德公园的蝴蝶一样?
  • @Amy 所有类都应该命名为Object,所有属性都应该命名为Property;这本质上是自我记录的,并强制执行“做一件事”规则。
  • @EdPlunkett 大声笑,我认为人们会明白所有实例都被神奇地抛到了风中。显然,如果这是我的实际代码,我会问其他问题。

标签: c# linq


【解决方案1】:

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

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

这是非常不理想的,所以如果你真的有真正的集合(即List),最好从最后一个索引开始删除项目。

【讨论】:

    【解决方案2】:

    写一个扩展方法:

    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&lt;Object&gt;,您将调用 TrimTrailing 使用

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

    不过要小心:在最坏的情况下,它会缓冲来自 items 的所有项目,然后丢弃缓冲区。

    【讨论】:

    • 与反向 + 跳过 + 反向相比,我喜欢这样的可读性
    【解决方案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;
        }
    }
    

    这将允许一次通过您的列表。

    【讨论】:

    • 为什么不只是if(list[i].Property != 0) break; list.RemoveAt(i);
    • @juharr 确定 - 这是任何合理的重构。
    • @juharr 有时最好将其反转以使其对用例更具可读性。
    • 感觉代码太多,i &gt; 0 似乎有点偏离for (int i = list.Count - 1; i &gt;=0 &amp;&amp; list[i].Property == 0; i--) list.RemoveAt(i);
    【解决方案4】:

    您可以使用FindLastIndex 查找最后一个非0 索引,并使用Take 查找直到该位置的元素。

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

    【讨论】:

    • 这只会返回示例的第一项。
    • @juharr:没错,我现在明白了。谢谢。更新了答案。
    猜你喜欢
    • 2018-11-24
    • 1970-01-01
    • 2019-07-12
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    • 2020-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多