2016-07-18 4 views
2

나는 NodaTime.IsoDayOfWeek 일의 목록을 취하는 함수를 만들기 위해 C#을 사용하고 있습니다. 나는 입력을 연속 일 그룹으로 묶고 싶다. 목록이 폐쇄 루프 그래서, 일요일, 월요일 연속 것을요일 목록을 연속 일 그룹으로 그룹화

{ 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 } } 

주의 사항 :

는 예를 들어, 다음 목록은 다음과 같은 출력을 제공해야합니다. 또한 결과 목록은 첫 번째 날이 입력 목록에 포함되지 않은 날 (즉 전체 목록이 포함 된 경우 월요일)을 직접 따르도록 주문해야합니다.

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; 
} 

Mauricio Scheffer published a great extension method to group consecutive integers here:

그러나 나는 일요일과 월요일도 연속이기 때문에 그룹 일이를 수정하는 방법을 알아낼 수 없습니다. 일요일과 월요일도 연속 된 것으로 간주되는 연속 일을 어떻게 그룹화 할 수 있습니까?

+1

[이 확인] (http://stackoverflow.com/questions/4681949/use-linq-to-group-a-sequence-of-numbers-with-no-gaps) - 사랑 DTB의 대답 - 자신의 필요에 어떻게 적응할 수 있는지 알아보십시오. –

답변

0

이것은 결국 해결할 해결책입니다. 나는 Linq를 여기에 사용했지만, 쉽게 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까지의 배열이고 시퀀스가 ​​아니라고 가정하고, 두 개의 루프를 사용하여 각각의 다음 숫자를 조건 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);