【问题标题】:Easiest way to get "next" element in a sequence?获取序列中“下一个”元素的最简单方法?
【发布时间】:2011-12-21 00:46:40
【问题描述】:

我有一个ICollection<SomeClass>

public class SomeClass
{
   public string Text { get; set; }
   public bool IsPreferred { get; set; }
}

里面的东西已经预定好了,所以“下一个”确实意味着什么。

在我的场景中,序列的内容如下所示:

[0] - “a”,假

[1] - “b”,真

[2] - “c”,假

我试图在IsPreferred == true 之后获取“下一个”元素。所以在上面,我想得到元素2,我想清除另一个IsPreferred值。

所以我想结束这个:

[0] - “a”,假

[1] - “b”,假

[2] - “c”,真

基本上是把喜欢的项目下移。

最好的方法是什么?我唯一能想到的就是创建一个新数组,将它们一一添加,跟踪首选数组的索引,然后在上述索引 +1 处获取元素。

有更好的想法吗?

【问题讨论】:

    标签: c# .net linq c#-4.0 icollection


    【解决方案1】:

    我会使用枚举器来遍历集合 - 这是 foreach 在幕后所做的:

    var enumerator = collection.GetEnumerator();
    
    while (enumerator.MoveNext())
    {
        if (enumerator.Current.IsPreferred)
        {
            var oldPreferred = enumerator.Current;
    
            if (enumerator.MoveNext())
            {
                oldPreferred.IsPreferred = false;
                enumerator.Current.IsPreferred = true;
            }
    
            break;
        }
    }
    

    这确实假设您想在找到第一个带有 IsPreferred 的项目后停止,如果这是最后一个项目,仍然清除 IsPreferred。

    编辑:修复了在单个项目的集合中 IsPreferred 始终设置为 false 的边缘情况

    【讨论】:

    • 我喜欢这个解决方案,因为 a) 它有效,b) 它相当高效,c) 我将它隐藏在一个名为 ShuffleDownToNextPreference 的扩展方法后面,所以我不必担心它,但是当我阅读使用它的代码时,我知道它在做什么。接受。
    • @RPM1984 和@TheEvilPenguin:我看到这种方法的唯一缺点是它总是将第一个项目的IsPreferred 设置为false,无论下一个项目是否存在。为避免这种情况,请参阅我的解决方案。
    • @AhmadMageed - 是的,这是真的。一个边缘案例,但仍然是真实的。抱歉 Pengiun - Ahmad 偷了你接受的答案。 :) +1 弥补它。
    • 谢谢 :) 我还是修好了边缘情况,否则会惹恼我
    【解决方案2】:

    由于ICollection<T> 没有为您提供索引器,我会选择更直接的解决方案而不是依赖 LINQ。

    假设你想(1)只要满足条件就停止,(2)只有在下一项存在时才改变值,这可以实现如下:

    bool isFound = false;
    SomeClass targetItem = null;
    foreach (var item in list)
    {
        if (isFound)
        {
            item.IsPreferred = true;
            targetItem.IsPreferred = false;
            break;
        }
        if (item.IsPreferred)
        {
            targetItem = item;
            isFound = true;
        }
    }
    

    【讨论】:

      【解决方案3】:

      我只能想到用 LINQ 做这件事的一种乱七八糟的方法:

      var x = collection.SkipWhile(z => !z.IsPreferred);
      SomeClass a = x.First();
      SomeClass b = x.Skip(1).First();
      
      a.IsPreferred = false;
      b.IsPreferred = true;
      

      这当然不包括错误检查,效率不高。


      另一种可能性(使用 LINQ)是使用 Ahmad Mageed 的解决方案(如下面的 cmets 建议):

      var x = collection.SkipWhile(z => !z.IsPreferred);
      SomeClass a = x.FirstOrDefault();
      SomeClass b = x.ElementAtOrDefault(1);
      
      if (a != null) a.IsPreferred = false;
      if (b != null) b.IsPreferred = true;
      

      【讨论】:

      • 如果您有很多项目,我的代码可能会更高效,但对于现代 PC,最好的解决方案通常是最易读的解决方案。如果在 6 个月后再次查看代码时这个更有意义,那就是更好的解决方案。
      • @Marlon 不错的 LINQ 方法,但如果下一个元素不存在,使用 SkipFirst 将引发异常。更好的方法是使用:SomeClass b = x.ElementAtOrDefault(1); 然后,如果 OP 想要更改值 only 如果下一项存在,则将最后 2 个赋值行放在此条件内:if (b != null) { ... }。当然,您提到没有错误检查:)
      • @AhmadMageed 感谢您的建议。我已经更新了我的答案。
      【解决方案4】:

      几个想法

      1. 如果您可以遍历集合,为什么我们不能在处理 i+1 之前将 i+2 值设置为 true?请务必确保 i+2 存在
      2. 另一个想法是扩展LinkedList 并使 current.next.next = true(如果存在)。

      【讨论】:

        【解决方案5】:

        既然你的集合是有序的,它可以是 IList 而不是 ICollection 吗?

        然后您可以创建一个扩展方法来为您提供某些谓词适用的值的索引:

        static IEnumerable<int> IndexWhere<T>(this IList<T> list, 
                                              Func<T, bool> predicate)
        {
            for(int i = 0; i < list.Count; i++)
            {
                if(predicate(list[i])) yield return i;
            }
        }
        

        假设您希望只有一个元素匹配,听起来其余的看起来就像这样:

        var preferredIndex = list.IndexWhere(x=>x.IsPreferred).Single();
        list[preferredIndex].IsPreferred = false;
        list[preferredIndex + 1].IsPreferred = true;
        

        【讨论】:

        • IEnumerable&lt;T&gt;ICollection&lt;T&gt; 没有索引器 - 这将如何工作?
        • 啊,你抓住了我;我总是将 ICollection 与 IList 混淆。
        • ICollection&lt;T&gt; 也没有索引器 - 试试你的代码,它不会编译。
        • 也就是说,ICollection 的语义意味着它的元素没有集合排序。如果你依赖有一个,你不应该使用 IList 吗? (我也会编辑我的评论。)
        • 是的,这是真的。我通常只使用ICollection&lt;T&gt; 处理序列,使用IEnumerable&lt;T&gt; 处理查询。我仍然认为您可以使用ICollection&lt;T&gt; 订购,否则他们不会暴露OrderBy 等(继承自IEnumerable&lt;T&gt;)。但我同意你的观点。
        猜你喜欢
        • 2011-08-06
        • 1970-01-01
        • 1970-01-01
        • 2011-09-13
        • 1970-01-01
        • 2012-10-21
        • 1970-01-01
        • 1970-01-01
        • 2011-12-31
        相关资源
        最近更新 更多