2012-06-30 21 views
9

tôi có một danh sách (giản thể)LINQ: về phía trước tìm kiếm tình trạng

[Kind]  [Name] 
null  E 
null  W 
4   T 
5   G 
6   Q 
null  L 
null  V 
7   K 
2   Z 
0   F 

Tôi cần {E, L} -> Items nơi Kind của họ == null và Kind tiếp theo == null quá

Giả sử rằng có một ID đang tăng lên và theo thứ tự.

Chuyển tiếp này có khả thi trong LINQ không?

Trả lời

9

Như thế này?

void Main() 
{ 
    List<SomeClass> list = new List<SomeClass>() { 
     new SomeClass() { Kind = null, Name = "E" }, 
     new SomeClass() { Kind = null, Name = "W" }, 
     new SomeClass() { Kind = 4, Name = "T" }, 
     new SomeClass() { Kind = 5, Name = "G" }, 
     ... 
    }; 

    var query = list.Where ((s, i) => 
     !s.Kind.HasValue && 
     list.ElementAtOrDefault(i + 1) != null && 
     !list.ElementAt(i + 1).Kind.HasValue); 
} 

public class SomeClass 
{ 
    public int? Kind { get; set; } 
    public string Name { get; set; } 
} 

Edit: Trộm cắp giải pháp @ Jeff Marcado để thực hiện một phương pháp mở rộng tương tự như việc sử dụng trên nhưng một chút sạch và không làm cho bạn đối phó với các chỉ số:

public static IEnumerable<TSource> WhereWithLookahead<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, bool> predicate) where TSource : class 
{ 
    using(var enumerator = source.GetEnumerator()) 
    { 
     if (!enumerator.MoveNext()) 
     { 
      //empty 
      yield break; 
     } 

     var current = enumerator.Current; 
     while (enumerator.MoveNext()) 
     { 
      var next = enumerator.Current; 

      if(predicate(current, next)) 
      { 
       yield return current; 
      } 

      current = next; 
     } 

     if (predicate(current, null)) 
     { 
      yield return current; 
     } 

    } 
} 

// Use: 
var query2 = list.WhereWithLookahead((current, next) => 
    !current.Kind.HasValue && 
    (next != null) && 
    next.Kind.HasValue); 
+2

tôi thấy một chỉ số ra khỏi phạm vi ngoại lệ trong giải pháp của bạn: nếu mục cuối cùng 'Loại' là' null' thì 'danh sách [i + 1]' sẽ ghi đè lên danh sách. – nemesv

+0

Đã chỉnh sửa: Cuộc gọi tốt. – Ocelot20

+0

Vẫn không hoàn hảo: thay thế 'i' bằng' i + 1' trong 'ElementAtOrDefault' và' ElementAt' để làm cho nó đúng. – nemesv

5

Đối với một cách tiếp cận chức năng, bạn có thể thực hiện một điều tra viên lookahead như vậy:

IEnumerable<Item> collection = ...; 
var lookahead = collection.Zip(collection.Skip(1), Tuple.Create); 

Điều tra viên sẽ lặp qua bộ dữ liệu của mỗi mục và mục sau đây. Điều này loại trừ mục cuối cùng trong bộ sưu tập. Sau đó, nó chỉ là vấn đề thực hiện truy vấn.

var query = collection.Zip(collection.Skip(1), Tuple.Create) 
    .Where(tuple => tuple.Item1.Kind == null && tuple.Item2.Kind == null) 
    .Select(tuple => tuple.Item1); 

Thật không may điều này sẽ rất kém hiệu quả. Bạn đang liệt kê chiều dài của bộ sưu tập hai lần và có thể rất tốn kém.

Sẽ tốt hơn để viết Enumerator của riêng bạn cho điều này, do đó bạn chỉ đi qua các bộ sưu tập trong một pass:

public static IEnumerable<TResult> LookAhead<TSource, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, TSource, TResult> selector) 
{ 
    if (source == null) throw new ArugmentNullException("source"); 
    if (selector == null) throw new ArugmentNullException("selector"); 

    using (var enumerator = source.GetEnumerator()) 
    { 
     if (!enumerator.MoveNext()) 
     { 
      //empty 
      yield break; 
     } 
     var current = enumerator.Current; 
     while (enumerator.MoveNext()) 
     { 
      var next = enumerator.Current; 
      yield return selector(current, next); 
      current = next; 
     } 
    } 
} 

Sau đó truy vấn trở thành:

var query = collection.LookAhead(Tuple.Create) 
    .Where(tuple => tuple.Item1.Kind == null && tuple.Item2.Kind == null) 
    .Select(tuple => tuple.Item1); 
+0

Tuyệt. Hy vọng bạn không nhớ tôi đã đánh cắp một số ý tưởng chung ở đây để cập nhật câu trả lời của tôi :) – Ocelot20

Các vấn đề liên quan