Hey I'm trying to figure out the most optimal way to grab a segment of data in a list. Based on their type Imagine I have the following data in a list:
public enum StepStatus {
Skipped,
NotStarted,
Completed,
}
public enum StepType
public class Steps {
StepStatus Status { get; set; }
StepType Type { get; set;}
// Other info
}
I have a list of all the steps and their statuses
//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
I want to grab all of the NotStarted, notifications, up to and including the first approval. So I want the to return this segment of the list
3, StepStatus.NotStarted, StepType.Notification
4, StepStatus.NotStarted, StepType.Notification
5, StepStatus.NotStarted, StepType.Approval
The Simplest way I can think to do this would be.
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);
I'm wondering if there is an easer/ more savy way to grab this segment using linq?
CodePudding user response:
Since we know you are using a List<T>
we can cheat a little and use the source IEnumerable
twice without much penalty.
Here is an extension method:
public static IEnumerable<T> TakePast<T>(this IEnumerable<T> items, Func<T, bool> posFn) => items.Take(items.TakeWhile(i => !posFn(i)).Count() 1);
Using this, you can do:
return steps.Where(s => s.StepStatus == StepStatus.NotStarted)
.TakePast(s => s.StepType == StepType.Approval);
Of course, that means you could just expand the extension method:
return steps.Where(s => s.StepStatus == StepStatus.NotStarted)
.Take(steps.Where(s => s.StepStatus == StepStatus.NotStarted).TakeWhile(s => s.StepType != StepType.Approval)).Count() 1);
I am assuming the only StepType
s are Notification
and Approval
since you didn't define the enum
.
Here is a generic implementation that enumerates any sequence once:
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;
}
}