2016-07-18 3 views
2

Я использую 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 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; 
} 

Однако я не могу понять, как изменить это группировать дней с воскресенья и понедельника также последовательны. Как я могу группировать последовательные дни, когда воскресенье и понедельник также считаются последовательными?

+1

[Проверьте это] (http://stackoverflow.com/questions/4681949/use-linq-to-group-a-sequence-of-numbers-with-no-gaps) - ответ Loved DTB в - посмотреть, как вы можете адаптировать его к своей потребности –

ответ

0

Это решение, с которым я столкнулся. Я использовал Linq здесь, но его можно легко переписать без него. Я также написал обширный набор модульных тестов для этого, прокомментируйте, если вы хотите получить к ним доступ.

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

Образец ввода - это упорядоченный массив. Представьте, что ваш вход представляет собой массив от 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; 
     } 
    } 
1

Я более эффективно не использовать 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);