Linq:前瞻性的状况

时间:2012-06-30 19:08:02

标签: c# linq

我有一个清单(简化)

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

我需要{E,L} - >其Kind == null和下一个Kind == null的项目

假设有一个ID正在增加且按顺序。

这在Linq看起来有可能吗?

2 个答案:

答案 0 :(得分:9)

喜欢这个吗?

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

编辑:窃取@Jeff Marcado的解决方案,实现类似于上述用法的扩展方法,但有点干净,而不是让你处理索引:

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

答案 1 :(得分:5)

对于功能方法,您可以像这样实现一个先行枚举器:

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

枚举器将迭代每个项目的元组,它是以下项目。这排除了集合中的最后一项。然后,这只是执行查询的问题。

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

不幸的是,效率非常低。你要两次计算集合的长度,而且非常昂贵。

为此编写自己的枚举器会更好,所以你只能在一遍中浏览集合:

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

然后查询变为:

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