2011-02-08 32 views
4

什么是适用以下使用LINQ序列中的最可读的方式:LINQ到对象:TakeWhileOrFirst

TakeWhile elements are valid but always at least the first element 

编辑:我已经更新了标题,更精确。我很抱歉有什么困惑,下面的答案肯定会教给我一些东西!

预期的行为是这样的:Take while元素有效。如果结果是一个空序列,无论如何要采取第一个元素。

+0

我已经更新了这个问题。查看修改。对困惑感到抱歉。 – asgerhallas 2011-02-09 09:52:02

回答

1

根据我的说法,手动执行效率最高,以确保不会超过必要的枚举。

public static IEnumerable<TSource> TakeWhileOrFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
{ 
    using (var enumerator = source.GetEnumerator()) 
    { 
     if (!enumerator.MoveNext()) 
      yield break; 
     TSource current = enumerator.Current; 
     yield return current; 
     if (predicate(current)) 
     { 
      while (enumerator.MoveNext() && predicate(current = enumerator.Current)) 
       yield return current; 
     } 
    } 
} 

而完成的缘故,包括索引过载:

public static IEnumerable<TSource> TakeWhileOrFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate) 
{ 
    using (var enumerator = source.GetEnumerator()) 
    { 
     if (!enumerator.MoveNext()) 
      yield break; 
     TSource current = enumerator.Current; 
     int index = 0; 
     yield return current; 
     if (predicate(current, index++)) 
     { 
      while (enumerator.MoveNext() && predicate(current = enumerator.Current, index++)) 
       yield return current; 
     } 
    } 
} 
4

以下工作*,似乎相当不错可读的对我说:

seq.Take(1).Concat(seq.TakeWhile(condition).Skip(1)); 

有可能是一个更好的办法,不知道。

*与感谢@Jeff米为校正

+3

你需要一个`IEnumerable <>`来指定`Concat()`。我会用`Take(1)`而不是`First()`。 – 2011-02-08 23:41:24

+0

@Rune:好点! – asgerhallas 2011-02-09 09:25:21

+0

@Rune:请注意,我跳过采集集合中的第一个值,而不是跳过集合中的第一个值,然后在匹配条件时进行。如果第一个是不匹配的并且以下是,则只返回第一个。 – 2011-02-09 20:01:10

5

我想,这使得意图非常明确:

things.TakeWhile(x => x.Whatever).DefaultIfEmpty(things.First()); 

我更早,更详细的解决方案:

var query = things.TakeWhile(x => x.Whatever); 
if (!query.Any()) { query = things.Take(1); } 
1

免责声明: 这是杰夫女士好的答案的变化,因此只是为了展示代码使用do-while代替。它仅作为Jeffs答案的扩展提供。

public static IEnumerable<TSource> TakeWhileOrFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
{ 
    using (var enumerator = source.GetEnumerator()) 
    { 
     if (!enumerator.MoveNext()) 
      yield break; 
     var current = enumerator.Current; 
     do{ 
      yield return current 
     } while (predicate(current) && 
       enumerator.MoveNext() && 
       predicate(current = enumerator.Current)); 
    } 
} 
当然

这是一个风格问题,我个人喜欢有我的条件逻辑是可能的,但双用谓词可能很难把握,可以是一个轻微的性能猪(视的尽可能低的嵌套级别在优化和分支预测)