Saroop Trivedi
Saroop Trivedi

Reputation: 2275

Split the Master List base on child List LINQ

I have list classes as below. I need to slipt List<TimeSheet> base on List<Activity> count. If activity is greater than 8 then List split by two sublists along with List 0-7 and 7-onward. remain value should be the same in List like employee name and activity hours.

More Detail: I am adding some more detail. Time has only one entry but internal Activity has 10 entries. I need to split the Time with two lists. the first-time list consumes 0-7 activity and another time list consumes 8-onward. So EmployeeName should be the same in both the child list. only Activity lists are different. In new lists, One list has 8 activity entries and another has 2 activity entries.

List<TimeSheet> Time;

сlass TimeSheet
{
    public string employeeName {get;set;}
    public List<Activity> activity {get;set;}
    public List<ActivityHours> ActivityHours{get;set;}
}

class Activity
{
    public string Id {get;set;}
    public string Activity {get;set;}
}

class ActivityHours
{
    public string ActivityId {get;set;}
    public string hours {get;set;}
}

Upvotes: 0

Views: 168

Answers (3)

Enigmativity
Enigmativity

Reputation: 117154

This seems rather simple with Take() and Skip().

List<TimeSheet> result = 
    Time
        .SelectMany(t => new []
        {
            new TimeSheet()
            {
                employeeName = t.employeeName,
                activity = t.activity.Take(8).ToList(),
                ActivityHours = t.ActivityHours,
            },
            new TimeSheet()
            {
                employeeName = t.employeeName,
                activity = t.activity.Skip(8).ToList(),
                ActivityHours = t.ActivityHours,
            },
        })
        .Where(t => t.activity.Any())
        .ToList();

Upvotes: 0

Anders H
Anders H

Reputation: 411

The follwing is also doing the job, but in a different way than @Bizhan's solution

     List<TimeSheet> SplitList = 
        TimeList.Aggregate(new List<TimeSheet>(), 
                           (accList, timeSheet) =>
                           {
                              if ((timeSheet.Activities?.Count ?? 0) > 8)
                              {
                                 accList.Add(new TimeSheet 
                                             { 
                                                EmployeeName = timeSheet.EmployeeName, 
                                                ActivityHours = timeSheet.ActivityHours.ToList(), 
                                                Activities = timeSheet.Activities.GetRange(0, 8) 
                                             });
                                 accList.Add(new TimeSheet 
                                             { 
                                                EmployeeName = timeSheet.EmployeeName, 
                                                ActivityHours = timeSheet.ActivityHours, 
                                                Activities = timeSheet.Activities.GetRange(8, timeSheet.Activities.Count - 8) 
                                             });
                              }
                              else
                              {
                                 accList.Add(timeSheet);
                              }
                              return accList;
                           },
                           accList => accList);

Upvotes: 1

Bizhan
Bizhan

Reputation: 17115

If you don't care about ActivityHours having the same reference across different chunks of activity you can use this code:

var newTime = Time
    .SelectMany(t =>
        Enumerable
        .Range(0, (t.activity.Count - 1) / 8)
        .Select(i => new TimeSheet
        {
            employeeName = t.employeeName,
            ActivityHours = t.ActivityHours,
            activity = t.activity.GetRange(i * 8, Math.Min(8, t.activity.Count))
        }))
    .ToList();

Note that Enumerable.Range(0, count) returns an Enumerable<int> like [0, 1,... , count-1]

and (t.activity.Count - 1) / 8 is to not have a TimeSheet at the end with an empty activity in case the count was multiple of 8.

activity.GetRange(i, count) returns an Enumerable<Activity> with size of count starting from 'i'th element of activity

and Math.Min(8, t.activity.Count) is to prevent argument out of range.

if you want to make them independent, you can clone it:

var newTime = Time
    .SelectMany(t =>
        Enumerable
        .Range(0, (t.activity.Count - 1) / 8)
        .Select(i => new TimeSheet
        {
            employeeName = t.employeeName,
            ActivityHours = 
                (t.activity.Count < 8) ? 
                t.ActivityHours : 
                t.ActivityHours.Select(h => new ActivityHours
                {
                    ActivityId = h.ActivityId,
                    hours = h.hours
                }).ToList(),
            activity = t.activity.GetRange(i * 8, Math.Min(8, t.activity.Count))
        }))
    .ToList();

Upvotes: 1

Related Questions