4thSpace
4thSpace

Reputation: 44312

Getting last x consecutive items with LINQ

My question is similar to this one: Finding Consecutive Items in List using Linq. Except, I'd like to get the last consecutive items that have no gaps. For example:

2, 4, 7, 8

output

7,8

Another example:

4,5,8,10,11,12

output

10,11,12

How can that be done?

Upvotes: 1

Views: 1048

Answers (3)

yamen
yamen

Reputation: 15618

I realise this is both late and wordy, but this is probably the fastest method here that still uses LINQ.

Test lists:

var list1 = new List<int> {2,4,7,8};
var list2 = new List<int> {4,5,8,10,11,12,15};

The method:

public List<int> LastConsecutive(List<int> list)
{
    var rev = list.AsEnumerable().Reverse();

    var res = rev.Zip(rev.Skip(1), (l, r) => new { left = l, right = r, diff = (l - r) })
                 .SkipWhile(x => x.diff != 1)
                 .TakeWhile(x => x.diff == 1);

    return res.Take(1).Select(x => x.left)
              .Concat(res.Select(x => x.right))
              .Reverse().ToList();
}

This one goes from back to front and checks elements pairwise, only taking elements from when they start being consecutive (the SkipWhile) until they end being consecutive (the TakeWhile).

Then it does some work to pull the relevant pairwise numbers out (left number from the 'original' list and then all the right numbers), and reverses it back. Similar efficiency to the imperative version, but in my opinion simpler to read because of LINQ.

Upvotes: 1

Tim Schmelter
Tim Schmelter

Reputation: 460028

This works and is probably easier and more efficient than LINQ in this case:

var list = new[] { 2, 4, 7, 8 };
List<int> lastConsecutive = new List<int>();
for (int i = list.Length - 1; i > 0; i--)
{
    lastConsecutive.Add(list[i]);
    if (list[i] - 1 != list[i - 1])
        break;
    if(i==1 && list[i] - 1 == list[i - 1]) // needed since we're iterating just until 1
        lastConsecutive.Add(list[0]);
}
lastConsecutive.Reverse();

Upvotes: 3

spender
spender

Reputation: 120380

I'm assuming in that you want the last consecutive sequence with more than one member... so from the sequence

{4, 5, 8, 10, 11, 12, 15} 

you're expecting the sequence:

{10, 11, 12}

I've indicated the line to remove if the last sequence is permitted to have only a single member, giving a sequence of

{15}

Here's the linq:

new[] {4, 5, 8, 10, 11, 12, 15}
    .Select((n,i) => new {n, i})
    .GroupBy(x => x.n - x.i) //this line will group consecutive nums in the seq
    .Where(g => g.Count() > 1) //remove this line if the seq {15} is expected
    .Select(x => x.Select(xx => xx.n))
    .LastOrDefault()

There's a hidden assumption here that the numbers of the sequence are in ascending order. If this isn't the case, it will be necessary to enroll the powers of microsoft's extension method for finding contiguous items in a sequence. Let me know if this is the case.

Upvotes: 10

Related Questions