将一周中的天数列表分组为连续的天数组



我正在使用C#创建一个函数,该函数占用NodaTime.IsoDayOfWeek天的列表。我想将输入分组为连续几天的组。

例如,以下列表应给出以下输出:

{ Mon, Tue } => { { Mon, Tue } }
{ Mon, Wed } => { { Mon }, { Wed } }
{ Mon, Tue, Fri, Sat } => { { Mon, Tue }, { Fri, Sat } }
{ Mon, Wed, Fri, Sun } => { { Sun, Mon }, { Wed }, { Fri } }
{ Mon, Tue, Wed, Thu, Fri, Sat, Sun } => { { Mon, Tue, Wed, Thu, Fri, Sat, Sun } }

请注意,星期日和星期一是连续的,所以列表是一个闭环。此外,对生成的列表进行排序,使第一天直接跟在未包含在输入列表中的一天之后(如果包含完整列表,则为星期一)。

Mauricio Scheffer在这里发表了一个对连续整数进行分组的伟大扩展方法:

public static IEnumerable<IEnumerable<int>> GroupConsecutive(this IEnumerable<int> list) {
    var group = new List<int>();
    foreach (var i in list) {
        if (group.Count == 0 || i - group[group.Count - 1] <= 1)
            group.Add(i);
        else {
            yield return group;
            group = new List<int> {i};
        }
    }
    yield return group;
}

然而,我不知道如何将其修改为团体日,因为周日和周一也是连续的。如果周日和周一也被视为连续的,我如何对连续的日子进行分组?

样本输入是有序数组。假设您的输入是从1到7的数组,而不是按顺序,您必须使用2个循环来根据条件Abs(current-next) == 1 || Abs(current-next) == 6找到下一个数字。这是我对您的解决方案的想法:

public static IEnumerable<IEnumerable<int>> GroupDay(IEnumerable<int> list)
    {
        List<int> input = new List<int>(list);
        while (input.Count > 0)
        {
            int i = input[0];
            var group = new List<int>();
            group.Add(i);
            input.RemoveAt(0);
            for (int j = 0; j < input.Count; )
            {
                if (Math.Abs(group[group.Count - 1] - input[j]) == 1
                    || Math.Abs(group[0] - input[j]) == 6)
                {
                    group.Add(input[j]);
                    input.RemoveAt(j);
                }
                else
                {
                    j++;
                }
            }
            // Sort output
            group.Sort((x, y) => {
                if (Math.Abs(x - y) == 6)
                {
                    // Sunday and Monday case
                    return y - x;
                }
                else
                    return x - y;
            });
            yield return group;
        }
    }

我没有更有效地使用LINQ,但我认为这可以。这只是一个简单的控制台应用程序。

 public enum Days
        {
            Mon = 1,
            Tue,
            Wed,
            Thur,
            Fri,
            Sat,
            Sun
        }



public static IEnumerable<IEnumerable<int>> GroupDay(IEnumerable<int> ListOfDays)        
{
            List<List<int>> Response = new List<List<int>>();
            List<int> Queue = new List<int>();
            var ListToIterate = ListOfDays.Distinct().OrderBy(d => d).ToList();
            foreach (var item in ListToIterate)
            {
                if (Queue.Count == 0)
                {
                    Queue.Add(item);
                }
                else
                {
                    if ((item - 1) == Queue[Queue.Count - 1])
                    {
                        Queue.Add(item);
                    }
                    else if (item != (int)Days.Sun)
                    {
                        Response.Add(Queue);
                        Queue = new List<int>() { item };
                    }
                }
                if (item == ListToIterate.LastOrDefault())
                    Response.Add(Queue);
                //Handle Sunday
                if (item == (int)Days.Sun)
                {
                    //Check if Saturday exists, if exists then do not put sunday before Monday.
                    var FindSaturday = Response.Where(r => r.Contains((int)Days.Sat)).FirstOrDefault();
                    if (FindSaturday == null)
                    {
                        var FindMonday = Response.Where(r => r.Contains((int)Days.Mon)).FirstOrDefault();
                        if (FindMonday != null)
                        {
                            FindMonday.Insert(0, item);
                        }
                    }
                }
            }
            return Response;
        }

下面是我如何尝试一些用例的。

//List<int> ListOfDays = new List<int>() { DaysToNumber(Days.Mon), DaysToNumber(Days.Tue) };
            //List<int> ListOfDays = new List<int>() { DaysToNumber(Days.Mon), DaysToNumber(Days.Wed) };
            //List<int> ListOfDays = new List<int>() { DaysToNumber(Days.Mon), DaysToNumber(Days.Tue), DaysToNumber(Days.Fri), DaysToNumber(Days.Sat) };
            //List<int> ListOfDays = new List<int>() { DaysToNumber(Days.Mon), DaysToNumber(Days.Wed), DaysToNumber(Days.Fri), DaysToNumber(Days.Sun) };
            //List<int> ListOfDays = new List<int>() { DaysToNumber(Days.Mon), DaysToNumber(Days.Tue), DaysToNumber(Days.Wed), DaysToNumber(Days.Thur), DaysToNumber(Days.Fri), DaysToNumber(Days.Sat), DaysToNumber(Days.Sun) };
            List<int> ListOfDays = new List<int>() { DaysToNumber(Days.Mon),DaysToNumber(Days.Fri),  DaysToNumber(Days.Sun) };
            var ListToIterate = ListOfDays.Distinct().OrderBy(d => d).ToList();
            var result = GroupDay(ListToIterate);

这就是我最终采用的解决方案。我在这里使用过林克,但如果没有它,它可以很容易地重写。我还为此写了一套广泛的单元测试,如果你想访问它们,请发表评论。

using NodaTime;
using System.Collections.Generic;
using System.Linq;
namespace Domain.Extensions
{
    public static class IsoDayOfWeekExtensions
    {
        public static IReadOnlyList<IReadOnlyList<IsoDayOfWeek>> GroupConsecutive(this IList<IsoDayOfWeek> days)
        {
            var groups = new List<List<IsoDayOfWeek>>();
            var group = new List<IsoDayOfWeek>();
            var daysList = days.Distinct().OrderBy(x => (int)x);
            foreach (var day in daysList)
            {
                if (!group.Any() || (int)day - (int)group.Last() == 1)
                {
                    group.Add(day);
                }
                else
                {
                    groups.Add(group);
                    group = new List<IsoDayOfWeek>() { day };
                }
            }
            // Last group will not have been added yet. Check if the last group can be combined with the first group (Sunday and Monday are also consecutive!)
            if (group.Contains(IsoDayOfWeek.Sunday) && groups.Any() && groups.First().Contains(IsoDayOfWeek.Monday))
            {
                // Insert before the Monday so that the days are in the correct consecutive order.
                groups.First().InsertRange(0, group);
            }
            else
            {
                groups.Add(group);
            }
            return groups.Select(x => x.ToList()).ToList();
        }
    }
}

最新更新