Linq for objects: TakeWhileOrFirst

What would be the most readable way to apply the following to a sequence using linq:

TakeWhile elements are valid but always at least the first element 

EDIT: I clarified the title, to be more precise. I apologize for any confusion, the answers below definitely taught me something!

Expected Behavior: Take while element valid. If the result is an empty sequence, take the first element anyway.

+4
source share
4 answers

This would be most effectively implemented manually, as far as I can tell, to make sure that he will not list more than necessary.

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

And for the sake of completion, an overload including an index:

 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; } } } 
+1
source

I think this makes the intention perfectly clear:

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

My earlier, more detailed solution:

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

The following works * and seem to me well readable:

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

Maybe better, not sure.

* thanks @Jeff M for correction

+4
source

DISCLAIMER: This is a variation of Jeff Mass's good answer, and as such is intended only to show code, using do-while instead. It is provided only as an answer to Jeffs' question.

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

Of course, this is a matter of style, which I personally like to have the nesting level of my conditional logic as low as possible, but the double use of a predicate can be difficult to understand and can be a small performance hit (depending on the optimization and branching prediction)

+1
source

Source: https://habr.com/ru/post/1338954/


All Articles