2011-02-08 139 views
2

我已经检查了其他的帖子,包括Group by variable integer range using Linq分组为连续整数的范围

,但我没有发现任何类似我的问题......我想组到整数的范围,其中整数序列是不连续的。例如,如果我有一组1-100的连续整数,然后我跳过101,我想创建一条记录,记录#1和#100的日期,其中记录#1的日期是开始日期和#100是结束日期。

连续整数的每个范围都会创建一个新记录,以添加到指示范围开始和结束日期的记录列表中。如果范围仅包含一个整数值(例如,整数范围从1-100,102和104-200),则单个整数范围将具有相同的开始和结束日期。

有什么建议吗?

+2

你能给出样本输入和预期的输出吗? – 2011-02-08 18:38:10

+1

整数序列是否以排序开头? – 2011-02-08 18:38:58

回答

0

我不知道,如果你能做到这一点使用LINQ提供的功能,但以下功能:

public static IEnumerable<IEnumerable<T>> GroupWhile<T>(this IEnumerable<T> values, Func<Int32, Boolean> predicate) 
{ 
    var i = 0; 
    var currentSet = new List<T>(); 
    while (i < values.Count()) 
    { 
     if (predicate(i)) 
      currentSet.Add(values.Skip(i).First()); 
     else 
     { 
      yield return currentSet; 
      currentSet = new List<T>(new[] {values.Skip(i).First()}); 
     } 
     i++; 
    } 
    yield return currentSet; 
} 

使用方法如下:

var ints = new[] {1, 2, 4, 5, 7, 8}; 
var groups = ints.GroupWhile(i => i == 0 || ints[i - 1] == ints[i] - 1).ToList(); 

可以适应法案。

5

您可以扩展方法,将做到这一点:

static class EnumerableIntExtensions { 
    public static IEnumerable<IEnumerable<T>> ToContiguousSequences<T>(
     this IEnumerable<T> sequence, 
     Func<T, T> next 
    ) { 
     Contract.Requires(sequence != null); 
     Contract.Requires(next != null); 
     var e = sequence.GetEnumerator(); 
     if (!e.MoveNext()) { 
      throw new InvalidOperationException("Sequence is empty."); 
     } 
     var currentList = new List<T> { e.Current }; 
     while (e.MoveNext()) { 
      T current = e.Current; 
      if (current.Equals(next(currentList.Last()))) { 
       currentList.Add(current); 
      } 
      else { 
       yield return currentList; 
       currentList = new List<T> { current }; 
      } 
     } 
     yield return currentList; 
    } 
} 

用法:

var sequence = Enumerable.Range(1, 100) 
         .Concat(new[] { 102 }) 
         .Concat(Enumerable.Range(104, 97)); 
var sequences = sequence.ToContiguousSequences(n => n + 1); 
foreach(var contiguousSequence in sequences) { 
    Console.WriteLine(String.Join(", ", contiguousSequence.Select(n => n.ToString()))); 
} 

输出:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 
102 
104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200 
0

这里是我创建了一个可以帮助一个样本类。它将一系列整数转换为一组范围。该代码在LINQPad中工作。

void Main() 
{ 
    ContiguousNumberLine line = new ContiguousNumberLine(); 

    List<Int64> testItems = new List<Int64>() { 1, 2, 4, 5, 7, 9, 11, 10, 3, 6, 8, 8 }; 
    foreach(Int64 item in testItems) 
    { 
     line.Add(item); 
     line.numberLine.Dump(string.Format("After adding {0}", item)); 
    } 

    line.Add(new List<Int64>() { 12, 14, 15, 16 }); 
    line.numberLine.Dump("After adding 12, 14, 15, 16"); 
    line.Add(new List<Int64>() { 17, 18, 19, 20 }); 
    line.numberLine.Dump("After adding 17, 18, 19, 20"); 

    foreach(var item in line.numberLine) 
    { 
     Console.WriteLine(string.Format("{0}\t{1}", item.Key, item.Value)); 
    } 
} 

// Define other methods and classes here 
public class ContiguousNumberLine 
{ 
    public SortedList<Int64, Int64> numberLine { get; private set; } 

    public ContiguousNumberLine() 
    { 
     numberLine = new SortedList<Int64, Int64>(); 
    } 

    public bool IsPresent(Int64 input) 
    { 
     if (numberLine.ContainsKey(input)) 
     { 
      return true; 
     } 
     else 
     { 
      foreach(var numRange in numberLine) 
      { 
       if (numRange.Key > input) 
       { 
        return false; 
       } 
       if (numRange.Value < input) 
       { 
        continue; 
       } 
       if (numRange.Key <= input && input <= numRange.Value) 
       { 
        return true; 
       } 
      } 
      return false; 
     } 
    } 

    public bool IsPresent(List<Int64> input) 
    { 
     if (IsContiguous(input)) 
     { 
      return IsPresentRange(input.First(), input.Last()); 
     } 
     else 
     { 
      foreach(Int64 item in input) 
      { 
       if (this.IsPresent(item)) 
       { 
        return true; 
       } 
      } 
     } 
     return false; 
    } 

    public bool IsPresentRange(Int64 first, Int64 last) 
    { 
     // naive implementation 
     for(Int64 i = first; i <= last; i++) 
     { 
      if (this.IsPresent(i)) 
      { 
       return true; 
      } 
     } 
     return false; 
    } 

    public bool IsContiguous(List<Int64> input) 
    { 
     Int64? first = null; 
     Int64? last = null; 
     foreach(Int64 item in input) 
     { 
      if (first == null) 
      { 
       first = item; 
       last = item; 
       continue; 
      } 
      if (last.Value +1 == item) 
      { 
       last = item; 
       continue; 
      } 
      else 
      { 
       return false; 
      } 
     } 
     return true; 
    } 

    public bool Add(Int64 input) 
    { 
     if (IsPresent(input)) 
     { 
      return false; 
     } 
     else 
     { 
      //numberLine.Add(input, input); 
      // extend the last number of an existing range, then check if it bridges the gap to the next range 
      KeyValuePair<Int64, Int64>? itemToRemove1 = null; 
      KeyValuePair<Int64, Int64>? itemToRemove2 = null; 
      KeyValuePair<Int64, Int64> itemToAdd = new KeyValuePair<Int64, Int64>(input, input) ; 

      foreach(var numRange in numberLine) 
      { 
       if (numRange.Value + 1 == input) 
       { 
        itemToRemove1 = numRange; 
        itemToAdd = new KeyValuePair<Int64, Int64>(numRange.Key, numRange.Value + 1); 
        continue; 
       } else if (itemToRemove1 != null && numRange.Key -1 == input) 
       { 
        itemToRemove2 = new KeyValuePair<Int64, Int64>(numRange.Key, numRange.Value); 
        KeyValuePair<Int64, Int64> newRange = new KeyValuePair<Int64, Int64>(itemToAdd.Key, numRange.Value); 
        itemToAdd = newRange; 
        break; 
       } else if (numRange.Key - 1 == input) 
       { 
        itemToRemove1 = numRange; 
        itemToAdd = new KeyValuePair<Int64, Int64>(input, numRange.Value); 
        break; 
       } else if (numRange.Key > input) 
       { 
        itemToAdd = new KeyValuePair<Int64, Int64>(input, input); 
        break; 
       } 
      } 

      if (itemToRemove1 != null) 
      { 
       numberLine.Remove(itemToRemove1.Value.Key); 
      } 
      if (itemToRemove2 != null) 
      { 
       numberLine.Remove(itemToRemove2.Value.Key); 
      } 
      numberLine.Add(itemToAdd.Key, itemToAdd.Value); 
      return true; 
     } 
    } 

    public bool Add(List<Int64> input) 
    { 
     // check if we can use the optimized version 
     if (IsContiguous(input)) 
     { 
      return this.AddRange(input.First(), input.Last()); 
     } 
     else 
     { 
      if(IsPresent(input)) 
      { 
       return false; 
      } 
      foreach(Int64 item in input) 
      { 
       this.Add(item); 
      } 
      return true; 
     } 

    } 

    public bool AddRange(Int64 first, Int64 last) 
    { 
     //throw new NotImplementedException(); 
     if(IsPresentRange(first, last)) 
     { 
      return false; 
     } 
     // naive implementation below (TODO: write optimized version) 
     for(Int64 i = first; i <= last; i++) 
     { 
      this.Add(i); 
     } 
     return true; 
    } 

}