Merge Two List in a way to cover full hours

橙三吉。 提交于 2020-01-17 17:33:09

问题


I have two lists Schedule(Having Total Hours) And Registered (Having Time Intervals). I want to create a list that is having registered time covered with Schedule list total hours finished.

See the Upper image. The resultant list is covering Registered Times and ENd if it doesn't find any registration for left hours. It will just create further times according to hours left with their types.


回答1:


Take a look at this sample:

    class Task
    {
        public int duration;
        public string type;
        public int remaining;
        public void Reset()
        {
            remaining = duration;
        }
    }
    class Span
    {
        public int from;
        public int to;
        public int remaining;
        public int duration => to - from;
        public void Reset()
        {
            remaining = duration;
        }
    }

    struct Assignment
    {
        public Span span;
        public string type;
    }
    static IEnumerable<Assignment> AssignTasks(List<Task> tasks, List<Span> spans)
    {
        // add an infinite span to the end of list
        spans.Add(new Span()
        {
            from = spans.Last().to,
            to = int.MaxValue
        });
        // set remainings of tasks and spans by their total duration
        foreach (Task task in tasks) { task.Reset(); }
        foreach (Span span in spans) { span.Reset(); }
        // set current task and span
        int iTask = 0;
        int iSpan = 0;
        while (iTask < tasks.Count)
        {
            //find which is smaller: remaining part of current task, or 
            // remaining part of current span
            int assigning =
            tasks[iTask].remaining <= spans[iSpan].remaining ?
                tasks[iTask].remaining : spans[iSpan].remaining;
            // add a new assignment to results
            yield return new Assignment()
            {
                span = new Span()
                {
                    from = spans[iSpan].to - spans[iSpan].remaining,
                    to = spans[iSpan].to - spans[iSpan].remaining + assigning,
                },
                type = tasks[iTask].type
            };
            // update remaining parts of current task and span
            tasks[iTask].remaining -= assigning;
            spans[iSpan].remaining -= assigning;
            // update counters if nothing is left 
            if (tasks[iTask].remaining == 0)
                iTask++;
            if (spans[iSpan].remaining == 0)
                iSpan++;
        }

    }

In this piece of code, Task is equivalent to what you call "Scheduled", and Span is equivalent to "Registered". I removed From and To from Task because they seem irrelevant to the problem. I also added a Remaining field to both Task and Span classes, I use these to keep the unassigned part of tasks or spans since a portion of a task can be assigned to a portion of a span.

The key point, to make everything much easier, is adding an infinite span to the end of list of spans. Now there are more registered spans (resources) than our demands and we just need to simply assign them.

You can test it like this:

    static void Main(string[] args)
    {
        List<Task> tasks = new List<Task>()
        {
            new Task() {duration = 4, type = "A"},
            new Task() {duration = 2, type = "B"},
            new Task() {duration = 6, type = "C"},
            new Task() {duration = 8, type = "D"}
        };
        List<Span> spans = new List<Span>()
        {
            new Span() {from = 9, to = 10},
            new Span() {from = 11, to = 13},
            new Span() {from = 15, to = 20}
        };
        IEnumerable<Assignment> assignments = AssignTasks(tasks, spans);

        Console.WriteLine("Tasks: duration, type");
        foreach (Task task in tasks)
        {
            Console.WriteLine($"{task.duration}, {task.type}");
        }
        Console.WriteLine("\nSpans: from, to");
        foreach (Span span in spans)
        {
            Console.WriteLine($"{span.from}, {span.to}");
        }
        Console.WriteLine("\nResults: from, to, type");
        foreach (Assignment assignment in assignments)
        {
            Console.WriteLine($"{assignment.span.from}, {assignment.span.to}, {assignment.type}");
        }
        Console.ReadLine();
    }

The outcome is:

Tasks: duration, type
4, A
2, B
6, C
8, D

Spans: from, to
9, 10
11, 13
15, 20

Results: from, to, type
9, 10, A
11, 13, A
15, 16, A
16, 18, B
18, 20, C
20, 24, C
24, 32, D




回答2:


Here's my solution:

The following enum and classes represent the input format you requested.

public enum SType
{
    A,
    B,
    C,
    D
}
public class Schedule : Register
{
    public SType Type { get; set; }
    public Schedule(int from, int to, SType type) : base(from, to)
    {
        Type = type;
    }
}

public class Register
{
    public int From { get; set; }
    public int To { get; set; }
    public int TotalHours => To - From;
    public Register(int from, int to)
    {
        From = from;
        To = to;
    }
}

Then, following your sample input:

var scheduled = new List<Schedule>()
{
    new Schedule(0, 4, SType.A),
    new Schedule(4, 6, SType.B),
    new Schedule(6, 12, SType.C),
    new Schedule(12, 20, SType.D)
};

var registered = new List<Register>()
{
    new Register(9, 10),
    new Register(11, 13),
    new Register(15, 20)
};

This will retrieve the requested output:

var result = new List<Schedule>();
int elapsedHours = 0;

foreach (var s in scheduled)
{
    int hours = s.TotalHours;

    foreach (var r in registered)
    {
        if (elapsedHours <= r.To)
        {
            int from = Math.Max(elapsedHours, r.From);

            if (r.TotalHours <= hours)
            {
                elapsedHours = r.To;
                result.Add(new Schedule(from, elapsedHours, s.Type));
                hours -= (elapsedHours - from);
            }
            else
            {     
                elapsedHours = from + hours;
                result.Add(new Schedule(from, elapsedHours, s.Type));                      
                hours = 0;
            }
        }
    }

    if (hours > 0)
    {
        result.Add(new Schedule(elapsedHours, elapsedHours + hours, s.Type));
        elapsedHours += hours;
    }
}



回答3:


Using the following classes to represent your source data:

public class Schedule {
    public int From;
    public int To;
    public int TotalHours => To - From;
    public string Type;

    public Schedule(int from, int to, string type) {
        From = from;
        To = to;
        Type = type;
    }
}

public class Register {
    public int From;
    public int To;

    public Register(int from, int to) {
        From = from;
        To = to;
    }
}

Here is a for loop implementation that spreads each Schedule member across the Registered time intervals and then outputs the rest:

var ans = new List<Schedule>();

int currentRegistered = 0;
var rp = registered[currentRegistered];
var currentFrom = rp.From;

for (int curScheduled = 0; curScheduled < scheduled.Count; ++curScheduled) {
    var s = scheduled[curScheduled];
    for (var hours = s.TotalHours; hours > 0;) {
        if (currentFrom >= rp.To)
            rp = (++currentRegistered < registered.Count)
                    ? registered[currentRegistered]
                    : new Register(currentFrom, int.MaxValue);
        if (rp.From > currentFrom)
            currentFrom = rp.From;

        var newTo = (rp.To - currentFrom > hours) ? currentFrom + hours : rp.To;
        ans.Add(new Schedule(currentFrom, newTo, s.Type));
        hours -= newTo - currentFrom;
        currentFrom = newTo;
    }
}


来源:https://stackoverflow.com/questions/59405450/merge-two-list-in-a-way-to-cover-full-hours

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!