繁体   English   中英

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

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

我有这样一个列表:

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 ..

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

提前致谢。

这里使用GroupConsecutive扩展方法,您只需获取每个组的计数:

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

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

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<IList<T>>但这与您想要拥有的组的概念略有不同,因为您还想知道用于对项目序列进行分组的值。 遗憾的是, 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();
    }
}

现在使用非常简单:

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 }
]
    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个)。

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

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

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

暂无
暂无

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

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