【问题标题】:how to count continuous values in a list with linq如何使用linq计算列表中的连续值
【发布时间】:2014-02-24 08:26:20
【问题描述】:

我有一个这样的列表:

var query = Enumerable.Range(0, 999).Select((n, index) =>
        {
            if (index <= 333 || index >=777)
                return 0;
            else if (index <= 666)
                return 1;
            else
                return 2;
        });

那么,我能找到多少索引连续具有相同的值吗?例如;

query[0]=query[1]=query[2]=query[3]... = 0, query[334] = 1, query[777]=query[778]... = 0.

前 334 个索引有 0,所以第一个答案是 333。最后 223 个索引有 0,所以第二个答案是 223..

我怎样才能找到这些及其索引?

提前致谢。

【问题讨论】:

  • @SonerGönül,我回滚了编辑,因为它们改变了问题的含义......
  • 也许您应该保留对语法和格式的修复?
  • @Magus 我懒得把你的编辑合并成一个正确的。
  • @L.B ,我同意。谢谢。
  • 当相同的值出现在多个段中时会发生什么,它们是单独的结果,您是求和还是忽略较低的计数?

标签: c# linq list indexing


【解决方案1】:

您可以通过某个键为项目的连续分组创建扩展:

public static IEnumerable<IGrouping<TKey, T>> GroupConsecutive<T, TKey>(
    this IEnumerable<T> source, Func<T, TKey> keySelector)
{
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext()) 
            yield break;            
        else 
        {
            List<T> list = new List<T>();
            var comparer = Comparer<TKey>.Default;
            list.Add(iterator.Current);
            TKey groupKey = keySelector(iterator.Current);

            while (iterator.MoveNext())
            {
                var key = keySelector(iterator.Current);
                if (!list.Any() || comparer.Compare(groupKey, key) == 0)
                {
                    list.Add(iterator.Current);
                    continue;
                }

                yield return new Group<TKey, T>(groupKey, list);
                list = new List<T> { iterator.Current };
                groupKey = key;
            }

            if (list.Any())
                yield return new Group<TKey, T>(groupKey, list);
        }
    }
}

当然,您可以返回IEnumerable&lt;IList&lt;T&gt;&gt;,但这与您想要拥有的组概念略有不同,因为您还想知道哪个值用于对项目序列进行分组。不幸的是,IGrouping&lt;TKey, TElement&gt; 接口没有公开实现,我们应该创建自己的:

public class Group<TKey, TElement> : IGrouping<TKey, TElement>
{
    private TKey _key;
    private IEnumerable<TElement> _group;

    public Group(TKey key, IEnumerable<TElement> group)
    {
        _key = key;
        _group = group;
    }

    public TKey Key
    {
        get { return _key; }
    }

    public IEnumerator<TElement> GetEnumerator()
    {
        return _group.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

现在用法很简单:

var groups =  query.GroupConsecutive(i => i) // produces groups
                   .Select(g => new { g.Key, Count = g.Count() }); // projection

结果:

[
  { Key: 0, Count: 334 },
  { Key: 1, Count: 333 },
  { Key: 2, Count: 110 },
  { Key: 0, Count: 222 }
]

【讨论】:

  • 我收到这样的错误:扩展方法必须在非泛型静态类中定义。我无法解决这个问题。
  • @team16sah 完全按照错误的说法 - 在 非通用静态 类中定义扩展方法。您的课程不是静态的,或者是通用的(或两者兼有)
  • 非常感谢。现在,没有错误。此外,我根据您的回答编辑了我的问题。如果有解决方案,您能帮我吗?
  • @team16sah 我的方法有一个小问题。将默认值保留为初始键值确实是个坏主意。现在一切正常。另外,我会将您的问题恢复为以前的编辑,因为 Update 确实是不同的问题(或者应该对我提出的答案发表评论)
【解决方案2】:

使用here 中的GroupConsecutive 扩展方法,您可以获得每个组的计数:

query.GroupConsecutive((n1, n2) => n1 == n2)
     .Select(g => new {Number = g.Key, Count = g.Count()})

【讨论】:

    【解决方案3】:
        public static IEnumerable<int> GetContiguousCounts<T>(this IEnumerable<T> l, IEqualityComparer<T> cmp)
        {
            var last = default(T);
            var count = 0;
            foreach (var e in l)
            {
                if (count > 0 && !cmp.Equals(e, last))
                {
                    yield return count;
                    count = 0;
                }
                count++;
                last = e;
            }
            if (count > 0)
                yield return count;
        }
    
        public static IEnumerable<int> GetContiguousCounts<T>(this IEnumerable<T> l)
        {
            return GetContiguousCounts(l, EqualityComparer<T>.Default);
        }
    
        static void Main(string[] args)
        {
            var a = new[] { 1, 2, 2, 3, 3, 3 };
            var b = a.GetContiguousCounts();
            foreach (var x in b)
                Console.WriteLine(x);
        }
    

    对于简单的测试用例,它输出 1、2、3。对于您的案例 334、333、110、222(最后一个值不是您在问题中询问的 223,因为您只有 999 个元素,而不是 1000 )。

    【讨论】:

      【解决方案4】:

      呃,这个怎么样,我能想到的最有效的实现方式。

       IEnuemrable<KeyValuePair<T, int>> RepeatCounter<T>(
               IEnumerable<T> source,
               IEqualityComparer<T> comparer = null)
      {
          var e = source.GetEnumerator();
          if (!e.MoveNext())
          {
              yield break;
          }
      
          comparer = comparer ?? EqualityComparer<T>.Default;
      
          var last = e.Current;
          var count = 1;
          while (e.MoveNext())
          {
              if (comparer.Equals(last, e.Current))
              {
                  count++;
                  continue;
              }
      
              yield return new KeyValuePair<T, int>(last, count);
              last = e.Current;
              count = 1;
          }
      
          yield return new KeyValuePair<T, int>(last, count);
      }
      

      仅枚举序列一次,仅在必要时分配变量。

      【讨论】:

        猜你喜欢
        • 2014-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-14
        • 2017-04-13
        • 2021-07-24
        • 2019-05-05
        相关资源
        最近更新 更多