Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#/Linq get sets with adjacent

Tags:

c#

linq

I have an ordered list like 0,1,2, 6,7, 10

I want to get the sets where the numbers are incremented by 1. I want the first number and the count or the series.

So I would get
start=0, count=3
start=6, count=2
start=10, count=1

How can I do that in C#?

The answer goes to what I feel is the nicest way. Readability is more important than performance for me.

like image 846
Karsten Avatar asked Aug 15 '11 10:08

Karsten


2 Answers

Defining a simple class to hold the results:

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

You can use a method like this:

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

I'm not keen on the magic value of int.MaxValue, but it saves extra logic around the first iteration.

Calling GetSets(new List<int>() { 0, 1, 2, 6, 7, 10 }) correctly gives the result you required.

like image 128
Dan Puzey Avatar answered Sep 18 '22 10:09

Dan Puzey


Try this (as "C# Statements" in LinqPad

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()
    ;
like image 43
Reb.Cabin Avatar answered Sep 22 '22 10:09

Reb.Cabin