C#2.0设计问题 - 从更大的列表创建子列表

时间:2022-09-25 10:23:41

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.

我有一大堆TODO任务。他们每个人都有一个估计的持续时间。我想将较大的列表分成较小的子列表,每个子列表最多包含4个小时的工作。

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?

将索引作为ref传递感觉很尴尬,而不是OOD。我真的很喜欢TODO列表,有点像流,所以我想知道我能做些什么类似的东西,但我有点像C#新手。我目前也仅限于C#2.0。有关优秀设计的快速指示吗?

4 个解决方案

#1


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;
}

#2


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.

LINQ可能会简化一些事情,但由于你使用的是C#2.0(我可能也认为是.NET 2.0),这似乎是最简单的解决方案。

#3


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);
}

#4


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.

此外,该解决方案将比Noldorin和Anton解决方案更好地分配项目。您最终可能会减少列表。

#1


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;
}

#2


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.

LINQ可能会简化一些事情,但由于你使用的是C#2.0(我可能也认为是.NET 2.0),这似乎是最简单的解决方案。

#3


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);
}

#4


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.

此外,该解决方案将比Noldorin和Anton解决方案更好地分配项目。您最终可能会减少列表。