2011-08-18 30 views
5

說我有一個整數以下數組:查找列表中的連續項使用LINQ

int[] numbers = { 1, 6, 4, 10, 9, 12, 15, 17, 8, 3, 20, 21, 2, 23, 25, 27, 5, 67,33, 13, 8, 12, 41, 5 }; 

我怎麼能寫一個LINQ查詢發現3個連續元素是,比方說,大於10?另外,如果我可以指定我想說第一,第二,第三等這樣的元素組,這將是很好的。

例如,LINQ查詢應該能夠識別: 12,15,17作爲第一組連續元素 23,25,27作爲第二組的 67,33,13作爲第三組

查詢應該返回給我第二組,如果我指定我想第二組的3個連續元素。

謝謝。

回答

7

更新:雖然Patrick在評論中指出在技術上並非「linq查詢」,但此解決方案可重複使用,靈活且通用。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication32 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int[] numbers = { 1, 6, 4, 10, 9, 12, 15, 17, 8, 3, 20, 21, 2, 23, 25, 27, 5, 67,33, 13, 8, 12, 41, 5 }; 

      var consecutiveGroups = numbers.FindConsecutiveGroups((x) => x > 10, 3); 

      foreach (var group in consecutiveGroups) 
      { 
       Console.WriteLine(String.Join(",", group)); 
      } 
     }   
    } 

    public static class Extensions 
    { 
     public static IEnumerable<IEnumerable<T>> FindConsecutiveGroups<T>(this IEnumerable<T> sequence, Predicate<T> predicate, int count) 
     { 
      IEnumerable<T> current = sequence; 

      while (current.Count() > count) 
      { 
       IEnumerable<T> window = current.Take(count); 

       if (window.Where(x => predicate(x)).Count() >= count) 
        yield return window; 

       current = current.Skip(1); 
      } 
     } 
    } 
} 

輸出:

12,15,17 
23,25,27 
67,33,13 

要獲得第二組,更改:

var consecutiveGroups = numbers.FindConsecutiveGroups((x) => x > 10, 3); 

要:

var consecutiveGroups = numbers.FindConsecutiveGroups((x) => x > 10, 3).Skip(1).Take(1); 

更新2在我們的職業調整之後由於numbers數組中的項數越來越大,下面的實現要快得多。

public static IEnumerable<IEnumerable<T>> FindConsecutiveGroups<T>(this IEnumerable<T> sequence, Predicate<T> predicate, int sequenceSize) 
{ 
    IEnumerable<T> window = Enumerable.Empty<T>(); 

    int count = 0; 

    foreach (var item in sequence) 
    { 
     if (predicate(item)) 
     { 
      window = window.Concat(Enumerable.Repeat(item, 1)); 
      count++; 

      if (count == sequenceSize) 
      { 
       yield return window; 
       window = window.Skip(1); 
       count--; 
      } 
     } 
     else 
     { 
      count = 0; 
      window = Enumerable.Empty<T>(); 
     } 
    } 
} 
+0

輝煌! - 非常感謝。 –

+0

漂亮的代碼,赫然快,但不是一個LINQ查詢。 –

+0

@Patrick,在技術上它不是一個「LINQ查詢」,但如果您刪除使用System.Linq的;行,這將無法正常工作,因此它是Linq,而不是以查詢的形式。而且這樣更靈活。以可重複使用的方式解決問題。 – Jim

3
int[] numbers = { 1, 6, 4, 10, 9, 12, 15, 17, 8, 3, 20, 21, 2, 23, 25, 27, 5, 67, 33, 13, 8, 12, 41, 5 }; 

var numbersQuery = numbers.Select((x, index) => new { Index = index, Value = x}); 

var query = from n in numbersQuery 
      from n2 in numbersQuery.Where(x => n.Index == x.Index - 1).DefaultIfEmpty() 
      from n3 in numbersQuery.Where(x => n.Index == x.Index - 2).DefaultIfEmpty() 
      where n.Value > 10 
      where n2 != null && n2.Value > 10 
      where n3 != null && n3.Value > 10 
      select new 
      { 
       Value1 = n.Value, 
       Value2 = n2.Value, 
       Value3 = n3.Value 
      }; 

爲了指定哪個組,你可以調用Skip方法

query.Skip(1) 
+0

非常好!一個大的LINQ查詢。 –

0

你爲什麼不試試這個擴展方法?

​​3210

改善唯一可以調用Count方法,因爲原因numbers枚舉(所以查詢失去了它的懶惰)。

無論如何,我確信這可能符合您的linqness要求。

編輯:或者這是少言版本(它不使用計數法):

public static IEnumerable<IEnumerable<T>> Consecutives<T>(this IEnumerable<T> numbers, int ranges, Func<T, bool> predicate) 
{ 
    var ordered = numbers.OrderBy(a => a); 
    return ordered.Where(predicate) 
        .Select((element, i) => ordered.Skip(i * ranges).Take(ranges)) 
        .TakeWhile(Enumerable.Any); 
} 
0

我必須爲雙打名單做到這一點。有一個上限和一個下限。這也不是一個真正的Linq解決方案,它只是一種實用的方法,我用腳本語言編寫,只實現C#的一個子集。

var sequence = 
[0.25,0.5,0.5,0.5,0.7,0.8,0.7,0.9,0.5,0.5,0.8,0.8,0.5,0.5,0.65,0.65,0.65,0.65,0.65,0.65,0.65]; 
double lowerLimit = 0.1; 
double upperLimit = 0.6; 
int minWindowLength = 3; 

// return type is a list of lists 
var windows = [[0.0]]; 
windows.Clear(); 

int consec = 0; 
int index = 0; 

while (index < sequence.Count){ 

     // store segments here 
     var window = new System.Collections.Generic.List<double>(); 

     while ((index < sequence.Count) && (sequence[index] > upperLimit || sequence[index] < lowerLimit)) {   
      window.Add(sequence[index]); 
      consec = consec + 1; 
      index = index +1; 
     } 

     if (consec > minWindowLength) { 
      windows.Add(window); 
     } 

     window = new System.Collections.Generic.List<double>(); 
     consec = 0; 

     index = index+1; 
} 

return windows;