Linq查询结果中项目之间存在某种期望的关系

时间:2017-02-19 19:38:21

标签: linq

linq查询Where子句可以将func应用于原始集中的项目,并返回bool以包含或不包含基于项目的项目& #39;的特点。好东西:

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>>)的类型参数   无法从使用中推断出来。尝试指定类型参数   明确。

编辑(解决方案)

事实证明,我是在正确的轨道上,但只是对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);
}

1 个答案:

答案 0 :(得分:3)

当单独处理每个项目时,Linq查询最有效。在尝试关联同一集合中的项目时,它不能很好地工作,而不必多次处理相同的集合和标准的linq运算符。

MoreLINQ库有助于提供额外的操作员来填补其中的一些空白。我不确定它提供的可以在此实例中使用的运算符,但我知道它有一个#plannername方法,它结合了迭代中的当前项和先前项。

一般情况下,对于这种情况,如果您需要自己推出,使用生成器编写它来生成序列要容易得多。作为通用扩展方法:

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

尽管如此,使用这些选项中的任何一个都很可能会使您的查询与查询提供程序不兼容。无论如何,这不是你想要表达的单一查询。