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

时间:2014-01-31 15:19:59

标签: c# linq list indexing

我有一个这样的清单:

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

如何找到这些及其索引?

提前致谢。

4 个答案:

答案 0 :(得分:1)

使用here中的GroupConsecutive扩展程序,您只需获取每个组的计数:

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

答案 1 :(得分: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<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 }
]

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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

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