0
\$\begingroup\$

I have two lists of Slots

public class Slot
{
    public DateTime Start { get; set; }

    public DateTime End { get; set; }

    public List<Service> Services { get; set; }
}

public class Service
{
    public int Id { get; set; }

    public int Duration { get; set; }
}

public class MergingClass
{
    public List<Slot> MergeSlots()
    {
        var mergedList = new List<Slot>();
        var list1 = new List<Slot>
                        {
                            new Slot
                                {
                                    Start = new DateTime(2018, 11, 1, 8, 0, 0),
                                    End = new DateTime(2018, 11, 1, 11, 0, 0),
                                    Services = new List<Service>
                                                   {
                                                       new Service
                                                           {
                                                               Duration = 20,
                                                               Id = 1
                                                           }
                                                   }
                                },
                            new Slot
                                {
                                    Start = new DateTime(2018, 11, 1, 12, 0, 0),
                                    End = new DateTime(2018, 11, 1, 16, 0, 0),
                                    Services = new List<Service>
                                                   {
                                                       new Service
                                                           {
                                                               Duration = 20,
                                                               Id = 1
                                                           }
                                                   }
                                }
                        };

        var list2 = new List<Slot>
                          {
                              new Slot
                                  {
                                      Start = new DateTime(2018, 11, 1, 8, 0, 0),
                                      End = new DateTime(2018, 11, 1, 11, 0, 0),
                                      Services = new List<Service>
                                                     {
                                                         new Service
                                                             {
                                                                 Duration = 30,
                                                                 Id = 2
                                                             }
                                                     }
                                  },
                              new Slot
                                  {
                                      Start = new DateTime(2018, 11, 1, 12, 0, 0),
                                      End = new DateTime(2018, 11, 1, 18, 0, 0),
                                      Services = new List<Service>
                                                     {
                                                         new Service
                                                             {
                                                                 Duration = 30,
                                                                 Id = 2
                                                             }
                                                     }
                                  }
                          };
        return mergedList;
    }
}

Start and End is block of time, that will be divided by service duration (service duration is int representing minutes).

So i have 2 lists (for 2 differenet services), and I need to merge them by Start and End dates into 3rd list (mergedList).

Method MergeSlots in this case should return:

mergedList = new List<Slot>
                    {
                        new Slot
                            {
                                Start = new DateTime(2018, 11, 1, 8, 0, 0),
                                End = new DateTime(2018, 11, 1, 11, 0, 0),
                                Services = new List<Service>
                                            {
                                                new Service
                                                    {
                                                        Duration = 20,
                                                        Id = 1
                                                    },
                                                new Service
                                                    {
                                                        Duration = 30,
                                                        Id = 2
                                                    }
                                            }
                            },
                        new Slot
                            {
                                Start = new DateTime(2018, 11, 1, 12, 0, 0),
                                End = new DateTime(2018, 11, 1, 16, 0, 0),
                                Services = new List<Service>
                                            {
                                                new Service
                                                    {
                                                        Duration = 20,
                                                        Id = 1
                                                    },
                                                new Service
                                                    {
                                                        Duration = 30,
                                                        Id = 2
                                                    }
                                            }
                            },
                        new Slot
                            {
                                Start = new DateTime(2018, 11, 1, 16, 0, 0),
                                End = new DateTime(2018, 11, 1, 18, 0, 0),
                                Services = new List<Service>
                                            {
                                                new Service
                                                    {
                                                        Duration = 30,
                                                        Id = 2
                                                    }
                                            }
                            }
                    };

Of course, both lists of slots come from a system that I can not influence and they will be different each time.

I tried to do it step by step, but solution is huge and ugly and error prone:

foreach (var slot in list2)
{
    var slotWithStartInList1 = list1.FirstOrDefault(x => x.Start <= slot.Start && x.End > slot.Start);
    if (slotWithStartInList1 != null)
    {
        if (slot.Start == slotWithStartInList1.Start)
        {
            if (slot.End == slotWithStartInList1.End)
            {
                slot.Services.AddRange(slotWithStartInList1.Services);
                mergedList.Add(slot);
                continue;
            }

            if (slot.End < slotWithStartInList1.End)
            {
                slot.Services.AddRange(slotWithStartInList1.Services);
                slotWithStartInList1.Start = slot.End;

                mergedList.Add(slot);
                mergedList.Add(slotWithStartInList1);
                continue;
            }

            slotWithStartInList1.Services.AddRange(slot.Services);
            slot.Start = slotWithStartInList1.End;

            mergedList.Add(slotWithStartInList1);
            mergedList.Add(slot);
            continue;
        }

        if (slot.End == slotWithStartInList1.End)
        {
            slotWithStartInList1.End = slot.Start;
            slot.Services.AddRange(slotWithStartInList1.Services);

            mergedList.Add(slotWithStartInList1);
            mergedList.Add(slot);
            continue;
        }

        if (slot.End > slotWithStartInList1.End)
        {
            var tempSlot = new Slot
                               {
                                   Start = slot.Start,
                                   End = slotWithStartInList1.End,
                                   Services = new List<Services>()
                               };
            tempSlot.Services.AddRange(slotWithStartInList1.Services);
            tempSlot.Services.AddRange(slot.Services);

            slotWithStartInList1.End = tempSlot.Start;
            slot.Start = tempSlot.End;

            mergedList.Add(tempSlot);
            mergedList.Add(slot);
            mergedList.Add(slotWithStartInList1);
            continue;
        }

        var tempSlot2 = new Slot
                           {
                               Start = slotWithStartInList1.Start,
                               End = slot.Start,
                               Services = new List<Services>()
                           };
        tempSlot2.Services.AddRange(slotWithStartInList1.Services);

        slot.Services.AddRange(slotWithStartInList1.Services);
        slotWithStartInList1.Start = slot.End;

        mergedList.Add(tempSlot2);
        mergedList.Add(slot);
        mergedList.Add(slotWithStartInList1);
        continue;
    }

    var slotWithEndInList1 = list1.FirstOrDefault(x => x.Start < slot.End && x.End >= slot.End);
    if (slotWithEndInList1 != null)
    {
        if (slot.End == slotWithEndInList1.End)
        {
            slot.End = slotWithEndInList1.End;
            slotWithEndInList1.Services.AddRange(slot.Services);

            mergedList.Add(slot);
            mergedList.Add(slotWithEndInList1);

            continue;
        }

        var tempSlot2 = new Slot
                            {
                                Start = slotWithEndInList1.Start,
                                End = slot.End,
                                Services = new List<Services>()
                            };
        tempSlot2.Services.AddRange(slotWithEndInList1.Services);
        tempSlot2.Services.AddRange(slot.Services);

        slot.End = tempSlot2.Start;
        slotWithEndInList1.Start = tempSlot2.End;

        mergedList.Add(tempSlot2);
        mergedList.Add(slot);
        mergedList.Add(slotWithEndInList1);
        continue;
    }

    mergedList.Add(slot);
}

foreach (var slot in list1)
{
    if (mergedList.Any(x => x.Start == slot.Start))
    {
        continue;
    }

    mergedList.Add(slot);
}

return mergedList;

I can add few private methods to avoid code duplication, but I wonder if there is better (cleaner, shorter) way to accomplish my goal?

Maybe some linq extensions?

\$\endgroup\$

closed as unclear what you're asking by t3chb0t, IEatBagels, Hosch250, Mast, Heslacher Nov 30 '18 at 5:41

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 4
    \$\begingroup\$ Could you explain what is the logic of the merge operation? In other words: what do you mean by merge? There can be different views: overwrite from left to right, from right to left, ignore, sum, extend, etc... \$\endgroup\$ – t3chb0t Nov 29 '18 at 13:58
  • \$\begingroup\$ Can you share the rest of the file as well? How are the functions used? In what place? \$\endgroup\$ – Mast Nov 29 '18 at 20:23
3
\$\begingroup\$

You have a lot of code to carefully, almost surgically, merge records from list2 into list1 based on a sorted order. Why not just (1) quickly merge the 2 lists without regards to order, followed immediately by (2) custom ordering? Something short like:

mergedList = list1.ToList().Union(list2).ToList();
mergedList = mergedList.OrderBy(x => x.StartTime).ThenBy(x => x.EndTime).ToList();

Also, I think Slot is too generic a name. I suggest TimeSlot instead.

You have no real checks when adding a time slot that Start occurs before End. You may also want to make sure all DateTime values are based on the same DateTimeKind. There is nothing in your code that prevents one time slot based on Utc and another based on Local.

Duration is an int representing minutes. This is ripe for confusion. You could rename the variable to be DurationInMinutes, but I would recommend using a TimeSpan instead. This makes the code more understandable with:

Duration = TimeSpan.FromMinutes(10);

Later you will probably be using that Duration with some conditional or other calculation with another DateTime and a TimeSpan is a great fit for that.

UPDATE MORE SUGGESTIONS

You are interested in a custom sort for your time slots, but I am guessing you want that sort always. You might want to consider making TimeSlot implement IEquatable<TimeSlot> and IComparable<TimeSlot>. You will need to add some methods to help out there, such as Equals, CompareTo, and GetHashCode.

IEquatable link

IComparable link

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.