【问题标题】:Managing ranges with LINQ challenge使用 LINQ 挑战管理范围
【发布时间】:2013-06-28 01:02:27
【问题描述】:

给定以下数字(代表星期几):1,2,3,4,5,6,7

以下是一些组合示例及其所需的输出:

  • 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 开始更好地设置范围)。

我不知道应该从哪里开始编写一个复杂的 ifed 函数,或者这可以使用 LINQ 函数之一来完成?
任何多汁的建议?

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

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

我有一个实体OpeningTimes 和一个字段DaysOfWeek,它告诉你这个实体的小时范围(在另一个属性中定义)适用于一周中的哪几天。

所以我使用上面的 get (要真正得到数字,我会使用 index + 1 添加 Select):

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

我认为这个想法是首先删除一个范围内的数字。

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

然后我会做一个格式化函数,上面写着if (item.First != item.Last) string.Join("-", item.First, Item.Last);

【问题讨论】:

    标签: linq aggregation range numeric-ranges


    【解决方案1】:

    有趣的问题。为了便于阅读,我决定使用一个表示范围的类:

    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()))
    

    【讨论】:

      【解决方案2】:

      这是我的想法:

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

      【讨论】:

      • 你也可以使用锯齿状数组
      • 稍后我会修改你的代码,但我想这就是我要使用的。
      【解决方案3】:

      这是我的看法。 (不幸的是,我无法阻止复制一个部分:

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

      【讨论】:

        猜你喜欢
        • 2013-12-24
        • 2021-01-12
        • 2021-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多