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);
我假设唯一的 StepType
是 Notification
和 Approval
因为你没有定义 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;
}
}
嘿,我正在尝试找出获取列表中的一段数据的最佳方法。根据他们的类型 假设我在列表中有以下数据:
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);
我假设唯一的 StepType
是 Notification
和 Approval
因为你没有定义 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;
}
}