2011-08-18 37 views
5

Nói rằng tôi có các mảng sau đây của các số nguyên:Tìm mục liên tiếp trong Danh sách sử dụng 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 }; 

Làm thế nào tôi có thể viết một truy vấn LINQ mà thấy 3 yếu tố liên tiếp được, nói, lớn hơn 10? Ngoài ra, nó sẽ là tốt đẹp nếu tôi có thể xác định tôi muốn nói nhóm đầu tiên, thứ hai, thứ ba vv của các yếu tố như vậy.

Ví dụ, truy vấn LINQ sẽ có thể xác định: 12,15,17 như nhóm đầu tiên của các yếu tố liên tiếp 23,25,27 như nhóm thứ hai 67,33,13 như nhóm thứ ba

Truy vấn sẽ trả về cho tôi nhóm thứ 2 nếu tôi chỉ định tôi muốn nhóm thứ 2 gồm 3 phần tử liên tiếp.

Cảm ơn.

Trả lời

7

CẬP NHẬT: Mặc dù không phải là "truy vấn LINQ" như Patrick chỉ ra trong nhận xét, giải pháp này có thể sử dụng lại, linh hoạt và chung chung.

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

Output:

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

Để có được nhóm thứ 2, thay đổi:

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

Để:

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

UPDATE 2 Sau khi tinh chỉnh này trong pro của chúng tôi sử dụng duction, việc thực hiện sau đây là nhanh hơn nhiều như số lượng các mục trong mảng số phát triển lớn hơn.

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

Tuyệt vời! - Cảm ơn rất nhiều. –

+0

Mã đẹp và ấn tượng nhanh, nhưng không phải là truy vấn LINQ. –

+0

@Patrick, về mặt kỹ thuật nó không phải là một "truy vấn LINQ" nhưng nếu bạn loại bỏ việc sử dụng System.Linq; dòng, điều này sẽ không hoạt động, do đó nó là LINQ, không phải dưới dạng truy vấn. Và nó linh hoạt hơn theo cách này. Giải quyết vấn đề theo cách tái sử dụng. – 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 
      }; 

Để xác định các nhóm, bạn có thể gọi Skip phương pháp

query.Skip(1) 
+0

Rất đẹp! một truy vấn LINQ lớn. –

0

Tại sao bạn không thử phương pháp mở rộng này?

public static IEnumerable<IEnumerable<T>> Consecutives<T>(this IEnumerable<T> numbers, int ranges, Func<T, bool> predicate) 
{ 
    IEnumerable<T> ordered = numbers.OrderBy(a => a).Where(predicate); 
    decimal n = Decimal.Divide(ordered.Count(), ranges); 
    decimal max = Math.Ceiling(n); // or Math.Floor(n) if you want 
    return from i in Enumerable.Range(0, (int)max) 
      select ordered.Skip(i * ranges).Take(ranges); 
} 

Điều duy nhất để cải thiện có thể là lời kêu gọi Count phương pháp vì nguyên nhân các liệt kê của numbers (để truy vấn mất sự lười biếng của nó).

Dù sao tôi chắc chắn điều này có thể đáp ứng các yêu cầu linqness của bạn.

EDIT: Ngoài ra đây là lời ít phiên bản (nó không tận dụng phương pháp Đếm):

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

tôi phải làm điều này cho một danh sách các đôi. Có giới hạn trên cũng như giới hạn dưới. Đây cũng không phải là một giải pháp LINQ thực sự, nó chỉ là một cách tiếp cận thực dụng mà tôi đã viết điều này bằng ngôn ngữ kịch bản mà chỉ thực hiện một tập con của 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; 
Các vấn đề liên quan