2017-02-19 17 views
1

A linq查詢Where子句可將func應用於原始集合中的項目,並返回bool以包含或不包含基於項目特徵的項目。好東東:Linq查詢結果中項目之間存在某種期望關係

var q = myColl.Where(o => o.EffectiveDate = LastThursday);

但是,如果我想找到一組項目,其中每個項目都以某種方式與最後一個項目相關?像:

var q = myColl.Where(o => o.EffectiveDate = thePreviousItem.ExpirationDate); 

你怎麼做一個Where(或其他linq功能)當前項目的「跳出去」?

這是我嘗試過的,試圖變得聰明。我做每一個項目的陣列,這樣我可以使用Aggregate功能:

public IQueryable<T> CurrentVersions 
{ 
    get => AllVersions 
     .Select(vo => new T[] { vo }) 
     .Aggregate((voa1, voa2) => voa1[0].BusinessExpirationDate.Value == voa2[0].BusinessEffectiveDate.Value ? voa1.Concat(voa2).ToArray() : voa1) 
     .SelectMany(vo => vo); 
} 

但是,這並不對SelectMany編譯:

類型參數的方法Enumerable.SelectMany<TSource, TResult>(IEnumerable<TSource>, Func<TSource, IEnumerable<TResult>>) 不能從推斷用法。嘗試明確指定類型參數 。

EDIT(液)

事實證明,我是正確的軌道上,但只是無所適從SelectMany一樣。我不需要它。我還需要將IQueryable更改爲IEnumerable,因爲我使用的是EF,並且在放開DbContext後無法查詢。所以,這裏是實際的解決方案。

public IEnumerable<T> CurrentVersions 
{ 
    get => AllVersions 
     .Select(vo => new T[] { vo }) 
     .Aggregate((voa1, voa2) => voa1[0].BusinessExpirationDate.Value == voa2[0].BusinessEffectiveDate.Value ? voa1.Concat(voa2).ToArray() : voa1); 
} 

回答

3

當每個項目單獨處理時,Linq查詢最有效。當試圖在同一個集合中關聯項目時,它不會很好地工作,而不必多次處理相同的集合和標準的linq運算符。

MoreLINQ庫幫助提供額外的運營商來填補這些差距。我不確定在這種情況下可以使用哪些運算符,但我知道它有一個Pairwise()方法,該方法將迭代中的當前項目和以前的項目組合在一起。

一般來說,對於這樣的情況,如果您需要推出自己的產品,使用發生器編寫產生序列會更容易。或者作爲通用擴展方法:

public static IEnumerable<TSource> WhereWithPrevious<TSource>(
      this IEnumerable<TSource> source, 
      Func<TSource, TSource, bool> predicate) 
{ 
    using (var iter = source.GetEnumerator()) 
    { 
     if (!iter.MoveNext()) 
      yield break; 

     var previous = iter.Current; 
     while (iter.MoveNext()) 
     { 
      var current = iter.Current; 
      if (predicate(current, previous)) 
       yield return current; 
     } 
    } 
} 

或者一個專門針對您正在嘗試解決的問題。

public static IEnumerable<MyType> GetVersions(IEnumerable<MyType> source) 
{ 
    using (var iter = source.GetEnumerator()) 
    { 
     if (!iter.MoveNext()) 
      yield break; 

     var previous = iter.Current; 
     while (iter.MoveNext()) 
     { 
      var current = iter.Current; 
      if (current.EffectiveDate == previous.ExpirationDate) 
       yield return current; 
     } 
    } 
} 

的另一種方法,而這在其他語言中的標準做法,但非常低效這裏將壓縮自身收集的一個偏移。

var query = Collection.Skip(1).Zip(Collection, (c, p) => (current:c,previous:p)) 
    .Where(x => x.current.EffectiveDate == x.previous.ExpirationDate) 
    ...; 

,並與所有的這麼說,任何使用這些選項將最有可能讓您的查詢與查詢提供不兼容。無論如何,這不是你想表達爲單個查詢的東西。

+0

謝謝!我想過拉鍊和跳過,但直到你看到它,你不能想象它打出來。當這個集合被減少到這個函數時,它只有幾行數據。所以低效率不會有太大影響。 – toddmo

+0

> Install-Package「System.ValueTuple」-IncludePrerelease – toddmo

+0

您也可以使用匿名對象代替元組。 –