简体   繁体   English

如何使用linq计算列表中的连续值

[英]how to count continuous values in a list with linq

I've a list like this: 我有这样一个列表:

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;
        });

So, Can I find how much indexes have same value continuously? 那么,我能找到多少索引连续相同的值吗? For example; 例如;

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

First 334 indexes have 0, so first answer is 333. Also Last 223 indexes have 0, so second answer is 223.. 前334个索引有0,所以第一个答案是333.另外最后223个索引有0,所以第二个答案是223 ..

How can I find these and their indexes? 我怎样才能找到这些及其索引?

Thanks in advance. 提前致谢。

Using the GroupConsecutive extension method from here you can just get the counts of each group: 这里使用GroupConsecutive扩展方法,您只需获取每个组的计数:

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

You can create extension for consecutive grouping of items by some key: 您可以通过某些键为项目的连续分组创建扩展名:

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);
        }
    }
}

Of course you can return IEnumerable<IList<T>> but that will be a little different from concept of group, which you want to have, because you also want to know which value was used to group sequence of items. 当然,您可以返回IEnumerable<IList<T>>但这与您想要拥有的组的概念略有不同,因为您还想知道用于对项目序列进行分组的值。 Unfortunately there is no public implementation of IGrouping<TKey, TElement> interface, and we should create our own: 遗憾的是, IGrouping<TKey, TElement>接口没有公开实现,我们应该创建自己的:

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();
    }
}

Now usage is very simple: 现在使用非常简单:

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

Result: 结果:

[
  { Key: 0, Count: 334 },
  { Key: 1, Count: 333 },
  { Key: 2, Count: 110 },
  { Key: 0, Count: 222 }
]
    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);
    }

For the simple test case, it outputs 1, 2, 3. For your case 334, 333, 110, 222 (the last value is not 223 as you asked in your question, because you only have 999 elements, not 1000). 对于简单的测试用例,它输出1,2,3。对于你的情况334,333,110,222(你问题中的最后一个值不是223,因为你只有999个元素,而不是1000个)。

erm, how about this, most efficient implementation I can think of. 嗯,这个怎么样,我能想到最有效的实现。

 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);
}

enumerates the sequence exactly once and only allocates variables when necessary. 完全枚举序列一次,只在必要时分配变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM