用LINQ挑战管理范围

问题描述:

给定以下数字(表示星期几):1,2,3,4,5,6,7用LINQ挑战管理范围

下面是一些组合的实施例和它们的期望输出:

  • 1,2,3,5,6,7 - >1-3,5-7
  • 1,3,5,7 - >1,3,5,7
  • 1,2,5,6 - >1,2,5,6
  • 1,2,3,6,7 - >1-3,6,7

这个想法是,连续3天或更多天成为一个范围,而单独或不跟随的日子单独呈现(或者更好地使范围从2开始)。

我不知道从哪里开始,我应该写一个复杂的if ed函数,或者这可以通过LINQ函数之一完成?
有没有多汁的建议?

我用数字来简化范围的想法,但在我的代码我有一个标记的枚举声明如下:

[Flags] 
public enum DaysOfWeek 
{ 
    Sunday = 0x1, 
    Monday = 0x2, 
    Tuesday = 0x4, 
    Wednesday = 0x8, 
    Thursday = 0x10, 
    Friday = 0x20, 
    Saturday = 0x40 
} 

我有一个实体OpeningTimes与现场DaysOfWeek,告诉什么天一周此实体的小时范围(在另一个属性中定义)适用于。

所以得到我用上面的(真正得到的数字我会使用索引+ 1加Select):

var days = Enum.GetValues(typeof(DaysOfWeek)) 
      .Cast<DaysOfWeek>() 
      .Where(dow => Model.DaysOfWeek.HasFlag(dow)); 

我认为这个想法是一个范围内,首先删除号码。

我相信我正在寻找一个接收前一个值的聚合函数,并且可以返回另一个值类型,所以我可以创建一个函数,如果当前值-1等于prev。值,我等待下一个值,直到范围不连续(或者如果元素代表自己),这是我产生时返回最后一个批量作为匿名对象,并开始在新的工作。

然后我会作出这样的表示if (item.First != item.Last) string.Join("-", item.First, Item.Last);

有趣的问题。我决定了可读性有一个表示范围的一类:

class NumberRange 
{ 
    public int Start { get; set;} 
    public int End { get; set;} 
    public override string ToString() 
    { 
     return Start == End ? Start.ToString() : String.Format("{0}-{1}",Start,End); 
    } 
} 

和扩展方法把有序整数的IEnumerable为范围的IEnumerable:

public static IEnumerable<NumberRange> ToRanges(this IEnumerable<int> numbers) 
{ 
    NumberRange currentRange = null; 
    foreach(var number in numbers) 
    { 
     if (currentRange == null) 
      currentRange = new NumberRange() { Start = number, End = number }; 
     else if (number == currentRange.End + 1) 
      currentRange.End = number; 
     else 
     { 
      yield return currentRange; 
      currentRange = new NumberRange { Start = number, End = number }; 
     } 
    } 
    if (currentRange != null) 
    { 
     yield return currentRange; 
    } 
} 

有了这样的地方,你可以得到的范围和格式,但你想要的:

String.Join(",", 
    new int[] { 1,2,3,5,7,8,9,11 } 
     .ToRanges() 
     .Select(r => r.ToString())) 

这里格式化函数是什么,我想:

void Main() 
{ 
    Console.WriteLine(AggregateString(new int[]{1,2,3,5,6,7})); //1-3,5-7 
    Console.WriteLine(AggregateString(new int[]{1,3,5,7}));  //1,3,5,7 
    Console.WriteLine(AggregateString(new int[]{1,2,5,6}));  //1,2,5,6 
    Console.WriteLine(AggregateString(new int[]{1,2,3,6,7 })); //1-3,6,7 
} 


string AggregateString(int[] ary) 
{ 
    List<List<int>> result=new List<List<int>>(); 
    ary.Aggregate((m,n)=> 
     { 
      if(m == n-1) 
      { 
      if(result.LastOrDefault()!=null && result.LastOrDefault().Last() ==m) 
       result.Last().Add(n); 
      else 
       result.Add(new List<int>{m,n}); 
      } 
      else 
      { 
       if(result.LastOrDefault()==null) 
        result.Add(new List<int>{m,n}); 
       else result.Add(new List<int>{n}); 
      } 
      return n; 
     }); 
    return string.Join(",", result.Select(s=>s.Count()>2? 
        string.Join("-",new string[]{s.First().ToString(),s.Last().ToString()}) : 
        string.Join(",",s.Select(x=>x.ToString()).ToArray())).ToArray()); 
} 
+0

你也可以使用锯齿阵列 – Shimmy

+1

锯齿阵列是不好的linq。所以我更喜欢List。 :) –

+0

稍后我会调整你的代码,但我认为这是我要使用的。 – Shimmy

以下是一张就可以了。 (不幸的是,我无法防止复制一个部分:

static IEnumerable<string> GetRange(IEnumerable<int> range) 
{ 
    using(IEnumerator<int> iter = range.GetEnumerator()) 
    if(iter.MoveNext()) 
    { 
     int last = iter.Current; 
     int start = iter.Current; 
     while(iter.MoveNext()) 
     { 
      int curr = iter.Current; 
      if (curr == last+1) 
      { 
       last = curr; 
       continue; 
      } 
      // found gap 
      if (start == last) // one isolated value 
      { 
       yield return start.ToString(); 
      } 
      else if (last - start == 1) // two in a row. 
      { 
       yield return start.ToString(); 
       yield return last.ToString(); 
      } 
      else 
      { 
       yield return string.Format("{0}-{1}", start,last); 
      } 
      start = curr; 
      last = curr;    
     } 

     if (start == last) // one isolated value 
     { 
      yield return start.ToString(); 
     } 
     else if (last - start == 1) // two in a row. 
     { 
      yield return start.ToString(); 
      yield return last.ToString(); 
     } 
     else 
     { 
      yield return string.Format("{0}-{1}", start,last); 
     } 
    } 
}