2011-08-18 43 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;