2017-02-19 20 views
1

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 делает. Мне это не нужно. Мне также необходимо было изменить на 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

Спасибо! Я думал о zip и пропустить, но пока вы не увидите его, вы не можете визуализировать его, чтобы ввести его. И к тому времени, когда набор будет уменьшен до этой функции, это всего лишь несколько строк данных. Таким образом, неэффективность не сильно влияет. – toddmo

+0

> Install-Package "System.ValueTuple" -IncludePrerelease – toddmo

+0

Вы также можете использовать анонимный объект вместо кортежа. –