使用Linq查找列表子集中的下一个元素

时间:2017-08-18 11:33:32

标签: c# linq

我有以下收藏

// The collection to query
    var stageTable = new List<Stage>
    {
    new Stage {StageId = 1, LifecycleId = 1, StageSeqNo = 1},
    new Stage {StageId = 2, LifecycleId = 1, StageSeqNo = 2},
    new Stage {StageId = 3, LifecycleId = 1, StageSeqNo = 3},
    new Stage {StageId = 4, LifecycleId = 1, StageSeqNo = 4},
    new Stage {StageId = 5, LifecycleId = 2, StageSeqNo = 1},
    new Stage {StageId = 6, LifecycleId = 2, StageSeqNo = 2},
    new Stage {StageId = 7, LifecycleId = 2, StageSeqNo = 3},
    new Stage {StageId = 8, LifecycleId = 2, StageSeqNo = 4},
    };

我尝试构建一个查询,该查询将返回给定Stage的下一个currentStage,但包含在LifecycleId定义的同一子集中,例如,给定{ {1}}我希望currentStage = 2返回Stage,但stageId = 3我会期望currentStage = 4返回null切换为LifecycleId价值2

这就是我所拥有的

 var lifecycleId = stageTable
                .Where(x => x.StageId == currentStageId)
                .Select(x => x.LifecycleId);

 var nextStage = stageTable
                .Where(s => s.LifecycleId == lifecycleId.First())
                .SkipWhile(s => s.StageId != currentStageId)
                .Skip(1).FirstOrDefault();

它似乎有效,但有没有办法在单个查询中执行此操作?

4 个答案:

答案 0 :(得分:1)

这使用带有谓词的FirstOrDefault的重载:

{
    var currentStageId = 3;

    // The collection to query
    var stageTable = new List<Stage> {
        new Stage {StageId = 1, LifecycleId = 1, StageSeqNo = 1},
        new Stage {StageId = 2, LifecycleId = 1, StageSeqNo = 2},
        new Stage {StageId = 3, LifecycleId = 1, StageSeqNo = 3},
        new Stage {StageId = 4, LifecycleId = 1, StageSeqNo = 4},
        new Stage {StageId = 5, LifecycleId = 2, StageSeqNo = 1},
        new Stage {StageId = 6, LifecycleId = 2, StageSeqNo = 2},
        new Stage {StageId = 7, LifecycleId = 2, StageSeqNo = 3},
        new Stage {StageId = 8, LifecycleId = 2, StageSeqNo = 4},
    };

    var nextStage = stageTable.FirstOrDefault(s => s.StageId > currentStageId && s.LifecycleId == stageTable.FirstOrDefault(s2=>s2.StageId==currentStageId)?.LifecycleId);
}

答案 1 :(得分:1)

如果您安装MoreLINQ NuGet package,则可以使用以下代码:

var currentStageId = 4;

var nextStage = stageTable.SkipWhile(z => z.StageId < currentStageId)
    .Lead(1, (x, y) => new { existing = x, next = y })
    .Take(1)
    .FirstOrDefault(z => z.next?.LifecycleId == z.existing.LifecycleId)?.next;

Console.WriteLine(nextStage?.StageId);

SkipWhile将跳过当前行之前的数据。

Lead将合并相邻的行(即将当前行和下一行放在一起)。

Take 1将确保我们只获得一行(代表当前行和下一行)。

如果第二行与第一行没有相同的FirstOrDefaultLifeCycleId将确保返回null。

答案 2 :(得分:0)

一种方法是首先获得当前阶段和下一阶段(无论周期如何),然后检查下一阶段是否属于同一周期:

var nextStage = stageTable.Where(s => s.StageId >= currentStageId)   //filter out all lower values
                .OrderBy(s=> s.StageId)                              //if the list is always ordered, this could be omitted
                .Take(2)                                             //enumeration now contains the currentid and the next id
                .GroupBy(s=>s.LifecycleId)                           //only from those 2 objects -> group by the lifecycle
                .First().Skip(1).FirstOrDefault();                   //the (first) group will only contain 2 items, if the lifecycle is the same

答案 3 :(得分:0)

我将介绍一种方法来迭代底层枚举和(可能为null)前一项:

@Test

可以使用此扩展程序提出以下解决方案:

public static class EnumerableExtras
{
    public static IEnumerable<Pair<T>> WithPrevious<T>(this IEnumerable<T> source)
        where T : class
    {
        T previous = null;

        foreach (var item in source)
        {
            yield return new Pair<T>(item, previous);
            previous = item;
        }
    }
}

public class Pair<T> where T : class
{
    public Pair(T current, T previous)
    {
        Current = current;
        Previous = previous;
    }

    public T Current { get; }
    public T Previous { get; }
}

对于值类型,可以使用可为空的var nextStage = stageTable .WithPrevious() .FirstOrDefault(pair => pair.Previous?.StageId == currentStageId && pair.Current.LifecycleId == pair.Previous?.LifecycleId) ?.Current; 属性定义相似的Pair<>类。