Reputation: 9339
I am looking for a good design/alogrithm/pattern for the following:
I have a large list of TODO tasks. Each one of them has an estimated duration. I want to break the larger list into smaller sublists, each sublist containing a max of 4 hours of work.
My current algorithm is something like this:
while( index < list.Count )
{
List<string> subList = CreateSublist( ref index );
SaveSubList(subList);
}
Passing the index in as a ref feels awkward and not OOD. I am really consuming the TODO list somewhat like a stream, so I'm wondering if there's something similar I could do, but I'm somewhat of a C# newbie. I am also currently limited to C# 2.0. Any quick pointers on a good design here?
Upvotes: 2
Views: 2166
Reputation: 48265
Here's my solution:
class Task
{
public string Name { get; set; }
public int Duration { get; set; }
}
class TaskList : List<Task>
{
public int Duration { get; set; }
public void Add(Task task, int duration)
{
this.Add(task);
Duration += duration;
}
}
private static IList<TaskList> SplitTaskList(IList<Task> tasks, int topDuration)
{
IList<TaskList> subLists = new List<TaskList>();
foreach (var task in tasks)
{
subLists = DistributeTask(subLists, task, topDuration);
}
return subLists;
}
private static IList<TaskList> DistributeTask(IList<TaskList> subLists, Task task, int topDuration)
{
if (task.Duration > topDuration)
throw new ArgumentOutOfRangeException("task too long");
if (subLists.Count == 0)
subLists.Add(new TaskList());
foreach (var subList in subLists)
{
if (task.Duration + subList.Duration <= topDuration)
{
subList.Add(task, task.Duration);
return subLists;
}
}
TaskList newList = new TaskList();
newList.Add(task, task.Duration);
subLists.Add(newList);
return subLists;
}
Notice that this is not the optimal solution ... that would go to a whole new level :)
Also, this solution will distribute the items a little better than Noldorin and Anton solutions. You might end up will fewer lists.
Upvotes: 0
Reputation: 59675
I would suggest to encapsulate this into a class.
SubListBuilder<WorkItem> slb = new SubListBuilder<WorkItem>(
workItems, sublist => sublist.Sum(item => item.Duration) <= 4);
This nicely allows to supply a predicate to control how the sublists are build. Then you can just get your results.
while (slb.HasMoreSubLists)
{
SaveList(slb.GetNextSubList());
}
Or maybe this way.
foreach (var subList in slb.GetSubLists())
{
SaveList(subList);
}
Upvotes: 0
Reputation: 147401
This should do the job:
public static List<List<Task>> SplitTaskList(List<Task> tasks)
{
List<List<Task>> subLists = new List<List<Task>>();
List<Task> curList = new List<Task>();
int curDuration; // Measured in hours.
foreach (var item in tasks)
{
curDuration += item.Duration;
if (curDuration > 4)
{
subLists.Add(curList);
curList = new List<Task>();
curDuration = 0;
}
curList.Add(item);
}
subLists.Add(curList);
return subLists;
}
LINQ would probably simplify things, but since you are using C# 2.0 (and likely also .NET 2.0 I would presume), this would seem like the most straightforward solution.
Upvotes: 0
Reputation: 115829
You can stuff everything in one method:
List<List<TodoTask>> GetTodoTasks(IEnumerable<TodoTask> tasks, int timeWindow)
{
List<List<TodoTask>> allTasks = new List<List<TodoTask>>();
List<TodoTask> tasks = new List<TodoTask>();
int duration = 0;
foreach(TodoTask task in tasks)
{
if(duration > timeWindow)
{
allTasks.Add(tasks);
duration = 0;
tasks = new List<TodoTask>();
}
tasks.Add(task);
duration += task.Duration;
}
allTasks.Add(tasks);
return allTasks;
}
Or, using iterators:
IEnumerable<List<TodoTask>> GetTodoTasks(IEnumerable<TodoTask> tasks, int timeWindow)
{
List<TodoTask> tasks = new List<TodoTask>();
int duration = 0;
foreach(TodoTask task in tasks)
{
if(duration > timeWindow)
{
yield return tasks;
duration = 0;
tasks = new List<TodoTask>();
}
tasks.Add(task);
duration += task.Duration;
}
yield return tasks;
}
Upvotes: 2