使用LINQ获取最后x个连续项目

时间:2012-04-27 23:17:12

标签: c# .net linq

我的问题与此类似:Finding Consecutive Items in List using Linq。除此之外,我想得到最后一个没有间隙的项目。例如:

2, 4, 7, 8

输出

7,8

另一个例子:

4,5,8,10,11,12

输出

10,11,12

怎么办呢?

3 个答案:

答案 0 :(得分:10)

我假设您希望最后一个连续序列具有多个成员......所以从序列

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

你期待着这个序列:

{10, 11, 12}

如果允许最后一个序列只有一个成员,则表示要删除的行,给出一系列

{15}

这是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()

这里有一个隐藏的假设,即序列的数字是按升序排列的。如果不是这种情况,则有必要为finding contiguous items in a sequence注册microsoft扩展方法的权限。如果是这种情况,请告诉我。

答案 1 :(得分:3)

在这种情况下,这可以比LINQ更容易和更有效:

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

答案 2 :(得分:1)

我意识到这既迟到又冗长,但这可能是仍然使用LINQ的最快方法。

测试清单:

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

方法:

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

这个从后到前并且成对地检查元素,仅从它们开始连续时(SkipWhile)获取元素,直到它们结束为连续(TakeWhile)。

然后它做了一些工作来拉出相关的成对数字(左边的数字从'原始'列表然后所有正确的数字),然后将其反转。与命令式版本类似的效率,但在我看来因为LINQ而更容易阅读。