Linq:前瞻性的状况

Ian*_*ink 9 c# linq

我有一个清单(简化)

[Kind]      [Name]
null        E
null        W
4           T
5           G
6           Q
null        L
null        V
7           K
2           Z
0           F
Run Code Online (Sandbox Code Playgroud)

我需要{E,L} - >项目,其种类== null,下一个种类== null

假设有一个ID正在增加并按顺序排列.

在Linq,这是前瞻性的吗?

Oce*_*t20 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; }
}
Run Code Online (Sandbox Code Playgroud)

编辑:窃取@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);
Run Code Online (Sandbox Code Playgroud)

  • 我在你的解决方案中看到索引超出范围异常:如果最后一项"Kind"为"null",那么`list [i + 1]`将对列表进行过度索引. (2认同)

Jef*_*ado 5

对于功能方法,您可以实现这样的前瞻枚举器:

IEnumerable<Item> collection = ...;
var lookahead = collection.Zip(collection.Skip(1), Tuple.Create);
Run Code Online (Sandbox Code Playgroud)

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

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

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

为此编写自己的枚举器会更好,所以你只能通过一次传递:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后查询变为:

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