I am using C# to create a function that takes in a list of NodaTime.IsoDayOfWeek
days. I want to group the input into groups of consecutive days.
For example, the following lists should give the following output:
{ 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 } }
Notice that Sunday and Monday are consecutive, so the list is a closed loop. In addition, the resulting lists should be ordered such that the first day directly follows a day that is not included in the input list (or Monday if the complete list is included).
Mauricio Scheffer published a great extension method to group consecutive integers here:
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;
}
However I can't figure out how to modify this to group days since Sunday and Monday are also consecutive. How can I group consecutive days where Sunday and Monday are also considered consecutive?
This is the solution I ended up going with. I've used Linq here, but it could be easily rewritten without. I also wrote an extensive set of unit tests for this, please comment if you would like access to them.
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();
}
}
}