C#/Linq获取相邻的set集合

6
我有一个有序列表,如下所示: 0、1、2、6、7、10
我想获取数字递增1的集合。我希望得到第一个数字和系列的数量。
所以我将得到 开始=0,计数=3 开始=6,计数=2 开始=10,计数=1
如何在C#中实现?
答案应该是最好的方式。对于我来说,可读性比性能更重要。

不确定为什么您将此标记为 Linq - 我不确定这是否适合使用 Linq。 - Dan Puzey
@Dan 也许你是对的,我们会看看是否可以使用聪明的 Linq 来解决这个问题。 - Karsten
@Karsten 我添加了一个类似 Linq 的建议。 - Reb.Cabin
4个回答

6

定义一个简单的类来存储结果:

    private class Set
    {
        public int Start = 0;
        public int Count = 0;
    }

您可以使用以下这种方法:

你可以像这样使用一个方法:

    private static IEnumerable<Set> GetSets(List<int> src)
    {
        List<Set> rtn = new List<Set>();
        int previous = int.MaxValue;

        foreach (int i in src)
        {
            if (i == previous + 1)
            {
                rtn[rtn.Count - 1].Count += 1;
            }
            else
            {
                rtn.Add(new Set() { Start = i, Count = 1 });
            }

            previous = i;
        }

        return rtn;
    }

我不是很喜欢使用 int.MaxValue 这个神奇的值,但它可以避免在第一次迭代时额外添加逻辑。
正确调用 GetSets(new List<int>() { 0, 1, 2, 6, 7, 10 }) 可以得到您需要的结果。

2
尝试使用以下代码(在LinqPad中作为“C#语句”):
var nums = new [] {0, 1, 2, 6, 7, 10};
Debug.Assert(nums.All(i => i >= 0));
Debug.Assert(nums.Zip(nums.Skip(1), (n1, n2) => (n1 < n2)).All(_ => _));
var @group = 0;
nums.Zip(nums.Skip(1).Concat(new [] {nums.Last ()}),
    (n1, n2) => Tuple.Create(
        n1,
        (n2 - n1) == 1 ? @group : @group++))
    .GroupBy (t => t.Item2)
    .Select (g => new {Group = g.Select(x => x.Item1), Count = g.Count()})
    .Dump()
    ;

哇,太棒了!我以前从未见过这样的查询! - Maciej Los

0

也许使用扩展方法会更加简洁

public static IEnumerable<IEnumerable<int>> GetConsecutiveCollections(this IEnumerable<int> source)
{
    var list = new List<int>();
    var start = source.Min() - 1;
    foreach (var i in source)
    {
        if (i == start + 1)
        {
            list.Add(i);
            start = i;
        }
        else
        {
            var result = list.ToList();
            list.Clear();
            list.Add(i);
            start = i;
            yield return result;
        }
    }
    yield return list;
}

然后以这种方式创建您的结果:

var result = x.GetConsecutiveCollections()
                .Select(c => new { Start = c.Min(), Count = c.Count()});

如果序列没有最小元素会怎样? - Eric Lippert
这段代码对输入的数据做了一些假设,因此不是生产质量,但我相信它提供了一个很好的起点。 - Dean Chalk

0

yield怎么样?

class GetSetsWithAdjacent
{
    public struct CountEm
    {
        public int start;
        public int count;

        override public string ToString()
        {
            return string.Format("start={0}, count={1}", this.start, this.count);
        }
    }

    static public IEnumerable<CountEm> GenCount(int[] inputs)
    {
        return GenCount(((IEnumerable<int>)inputs).GetEnumerator());
    }

    static public IEnumerable<CountEm> GenCount(IEnumerator<int> inputs)
    {
        if (inputs.MoveNext())
        {
            CountEm result = new CountEm {start = inputs.Current, count = 1 };

            while (inputs.MoveNext())
            {
                if (result.start + result.count == inputs.Current)
                {
                    result.count += 1;

                }
                else
                {
                    yield return result;
                    result = new CountEm { start = inputs.Current, count = 1 };

                }
            }

            yield return result;
        }
    }
}

class StackOverflow
{
    private static void Test_GetSetsWithAdjacent()
    {
        // https://dev59.com/6FrUa4cB1Zd3GeqPiUUB
        int[] inputs = { 0, 1, 2, 6, 7, 10 };

        foreach (GetSetsWithAdjacent.CountEm countIt in GetSetsWithAdjacent.GenCount(inputs))
        {
            Console.WriteLine(countIt);
        }
    }
    internal static void Test()
    {
        Test_GetSetsWithAdjacent();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接