Linq 按状态分割数据

Linq segmenting data by status

嘿,我正在尝试找出获取列表中的一段数据的最佳方法。根据他们的类型 假设我在列表中有以下数据:

public enum StepStatus {
    Skipped,
    NotStarted,
    Completed,
}

public enum StepType 

public class Steps {
    StepStatus Status { get; set; }
    StepType Type { get; set;}
    // Other info
} 

我有所有步骤及其状态的列表

//Data
    1, StepStatus.Skipped, StepType.Notification
    2, StepStatus.Completed, StepType.Notification
    3, StepStatus.NotStarted, StepType.Notification
    4, StepStatus.NotStarted, StepType.Notification
    5, StepStatus.NotStarted, StepType.Approval}
    6, StepStatus.NotStarted, StepType.Notification

我想获取所有 NotStarted 通知,包括第一次批准。所以我想要 return 列表的这一部分

3, StepStatus.NotStarted, StepType.Notification
4, StepStatus.NotStarted, StepType.Notification
5, StepStatus.NotStarted, StepType.Approval

我能想到的最简单的方法是。

var firstApprovalStep =  steps.FirstOrDefault(x => x.Status == StepStatus.NotStarted && x.Type == StepType.Approval);

if(null == firstApprovalStep)
{
   //If there are no pending approvals left return the pending notfications
   return steps.Reverse().TakeWhile(x => x.Status == StepStatus.NotStarted && x.Type == StepType.Notification);
}

//Find the element in the list with that index and grab all prior.
steps.GetNotStartedNotificationsPrior(firstStep);

我想知道是否有更简单/更精明的方法来使用 linq 获取该段?

因为我们知道您使用的是 List<T>,我们可以稍微作弊并使用来源 IEnumerable 两次而不会受到太大的惩罚。

这里有一个扩展方法:

public static IEnumerable<T> TakePast<T>(this IEnumerable<T> items, Func<T, bool> posFn) => items.Take(items.TakeWhile(i => !posFn(i)).Count()+1);

使用它,您可以:

return steps.Where(s => s.StepStatus == StepStatus.NotStarted)
            .TakePast(s => s.StepType == StepType.Approval);

当然,这意味着您可以扩展扩展方法:

return steps.Where(s => s.StepStatus == StepStatus.NotStarted)
            .Take(steps.Where(s => s.StepStatus == StepStatus.NotStarted).TakeWhile(s => s.StepType != StepType.Approval)).Count()+1);

我假设唯一的 StepTypeNotificationApproval 因为你没有定义 enum.

这里是一个通用的实现,可以枚举任何序列一次:

public static IEnumerable<T> TakePast<T>(this IEnumerable<T> items, Func<T, bool> posFn) {
    var ie = items.GetEnumerator();
    while (ie.MoveNext()) {
        yield return ie.Current;
        if (posFn(ie.Current))
            yield break;
    }
}