Home > Enterprise >  Linq segmenting data by status
Linq segmenting data by status

Time:04-29

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 StepTypes 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;
    }
}
  • Related