2016-02-22 17 views
5

Giả sử tôi đã mảng sau (trình tự của tôi đều được sắp xếp theo thứ tự tăng dần, và chứa các số nguyên dương)LINQ truy vấn để xác định các mảnh vỡ trong một loạt

var mySequence = new [] {1, 2, 3, 7, 8, 9, 15, 16, 17}; 

Tôi muốn viết một truy vấn LINQ để chọn liên tục số trong một chuỗi được coi là một nhóm. Vì vậy, trong ví dụ trên tôi sẽ nhận được {[1, 2, 3], [7, 8, 9], [15, 16, 17]}.

Tôi có thể viết một chuỗi foreach(), chạy qua từng phần tử và xem vị trí chuỗi đang nhảy và tạo nhóm ở đó. Nhưng có cách nào để làm điều đó không? Tôi có thể di chuyển mã số foreach() của mình vào một phương thức mở rộng mới để mã của tôi vẫn trông LINQy, nhưng tôi tự hỏi liệu có bất kỳ điều gì có sẵn trong System.Linq đã có cho điều đó hay không.

EDIT: Tôi đã tạo tiện ích mở rộng của riêng mình (như sau), nhưng Me.Name đã đưa ra một điều rất thông minh trong Câu trả lời của anh ấy.

internal class Sequence 
{ 
    public int Start { get; set; } 
    public int End { get; set; } 
} 

internal static class EnumerableMixins 
{ 
    public static IEnumerable<Sequence> GroupFragments(this IEnumerable<int> sequence) 
    { 
     if (sequence.Any()) 
     { 
      var lastNumber = sequence.First(); 
      var firstNumber = lastNumber; 

      foreach(var number in sequence.Skip(1)) 
      { 
       if (Math.Abs(number - lastNumber) > 1) 
       { 
        yield return new Sequence() { Start = firstNumber, End = lastNumber }; 

        firstNumber = lastNumber = number; 
       } 
       else 
       { 
        lastNumber = number; 
       } 
      } 


      yield return new Sequence() { Start = firstNumber, End = lastNumber }; 
     } 
    } 
} 

Trả lời

10

Bí quyết cũ để tìm các loại hòn đảo này là trừ chỉ mục và giá trị số. Kết quả sẽ đại diện cho các nhóm duy nhất. Sử dụng lựa chọn quá tải bao gồm các chỉ số:

var mySequence = new [] {1, 2, 3, 7, 8, 9, 15, 16, 17}; 

var groups = mySequence 
    .Select((val,ind) => new{val, group = val - ind}) 
    .GroupBy(v=>v.group, v=>v.val) 
    .Select(v=> v.ToList()).ToList(); 

(Dùng ToList đây, nhưng tất nhiên ToArray có thể được sử dụng nếu mảng được ưa thích)

+0

Đây là một thủ thuật dễ thương cho integers-- nếu kết quả của (số - index) là 0 thì con số này nằm ở vị trí mảng dự kiến, nếu con số này là số âm thì nó là nhiều trước khi nó được mong đợi. – Hogan

+0

Nếu chuỗi của tôi không bắt đầu bằng 1 thì sao? giả sử đây là chuỗi của tôi var x = new [] {20, 21, 22, 25,26,27, 30,31,32}; – fahadash

+1

@fahadash: điều đó cũng sẽ hoạt động, ý định là sự khác biệt giữa chỉ mục và giá trị lớn hơn nếu có khoảng cách giữa các giá trị. Đối với chuỗi đó: 20-0 = 20, 21-1 = 20, 22 -2 = 20, 25 -3 = 22, 26-4 = 22, v.v. Vì vậy, giá trị 'nhóm' nhảy từ 20 đến 22 –

1

Tôi hơi muộn nhưng dù sao tôi muốn chia sẻ ý tưởng. Bạn cũng có thể tạo ra một iterator như thế này:

public static class Extensions 
{ 
    public static IEnumerable<IEnumerable<int>> ContinuousNumbers(this IEnumerable<int> source) 
    { 
     using (var e = source.GetEnumerator()) 
     { 
      if (e.MoveNext()) 
      { 
       var list = new List<int> { e.Current}; 
       int temp = e.Current; 
       while (e.MoveNext()) 
       { 
        if (e.Current == temp+1) 
        { 
         list.Add(e.Current); 
         temp++; 
        } 
        else 
        { 
         yield return list; 
         list = new List<int> { e.Current}; 
         temp = e.Current; 
        } 
       } 

       if (list.Count > 0) 
       { 
        yield return list; 
       } 
      } 
     } 
    } 
} 

Một biến thể có thể sử dụng phương pháp Aggregate mở rộng:

var result = mySequence.Aggregate(new List<List<int>>(), 
            (r, current) => 
            { 
             if (r.Count==0 || (r.Last().Count>0 && r.Last().Last() != current-1)) 
              r.Add(new List<int> { current}); 
             else 
              r.Last().Add(current); 
             return r; 
            }); 
Các vấn đề liên quan