如何使用linq将以下方法应用于序列,使其更易读:
TakeWhile elements are valid but always at least the first element
编辑:我已更新标题,以更加精确。对于任何混淆的地方,我很抱歉,下面的答案肯定教会了我一些东西!
期望的行为是这样的:只要元素有效,就取出来。如果结果为空序列,那么仍然取第一个元素。
如何使用linq将以下方法应用于序列,使其更易读:
TakeWhile elements are valid but always at least the first element
编辑:我已更新标题,以更加精确。对于任何混淆的地方,我很抱歉,下面的答案肯定教会了我一些东西!
期望的行为是这样的:只要元素有效,就取出来。如果结果为空序列,那么仍然取第一个元素。
things.TakeWhile(x => x.Whatever).DefaultIfEmpty(things.First());
我之前的解决方案比较啰嗦:
var query = things.TakeWhile(x => x.Whatever);
if (!query.Any()) { query = things.Take(1); }
以下内容*,在我的看来读起来相当顺畅:
seq.Take(1).Concat(seq.TakeWhile(condition).Skip(1));
IEnumerable<>
才能使用Concat()
。我会用Take(1)
来代替First()
。 - Jeff Mercado据我所知,为了确保不进行不必要的枚举,最有效的实现方式是手动实现。
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;
}
}
}
免责声明: 这是Jeff Ms不错答案的变体,因此仅旨在展示使用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) );
}
}
yield break;
。 - Jeff Mercado