C# LINQ SkipWhile: Syntax and Behavior
c# linq skipwhile: C# LINQ SkipWhile skips leading elements while a condition holds, then returns the rest. Learn its syntax, indexed overload, and common pitfalls.
c# linq skipwhile requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
What SkipWhile Does
The SkipWhile operator in C# LINQ removes elements from the start of a sequence while a predicate returns true. Once the predicate returns false for an element, that element and everything after it are returned unchanged. The predicate is never evaluated again after the first failure.
int[] numbers = { 1, 3, 5, 2, 4, 6 }; IEnumerable<int> result = numbers.SkipWhile(n => n % 2 != 0); Console.WriteLine(string.Join(", ", result)); // Output: 2, 4, 6
The first three elements are odd, so n % 2 != 0 keeps returning true and those elements are skipped. At 2, the predicate returns false, and SkipWhile returns 2 and everything after it.
The single-argument overload takes a Func<T, bool>. A second overload also provides the element's index, which is useful when the decision depends on position.
The Stopping Rule: Why SkipWhile Is Not Where
The most common mistake is confusing SkipWhile with Where. Both take a predicate, but they behave differently:
Whereevaluates the predicate for every element and keeps the elements that match.SkipWhileevaluates the predicate only from the start of the sequence and stops at the first failure.
Consider:
int[] numbers = { 1, 3, 5, 2, 4, 6, 7 }; var skipped = numbers.SkipWhile(n => n % 2 != 0); var filtered = numbers.Where(n => n % 2 != 0);
skipped is 2, 4, 6, 7. The trailing 7 is odd, but SkipWhile already stopped evaluating at 2, so the 7 is included.
filtered is 1, 3, 5, 7. Where checked every element and kept only the odd ones.
The stopping rule is the defining behavior of SkipWhile. If you need to remove all elements that match a condition regardless of position, use Where. If you need to remove only a leading run of matching elements, use SkipWhile.
SkipWhile vs Skip
Skip and SkipWhile both remove elements from the start of a sequence, but they use different criteria:
| Operator | Criterion | Example |
|---|---|---|
Skip(n) | Fixed count | numbers.Skip(3) removes the first 3 elements |
SkipWhile(predicate) | Condition | numbers.SkipWhile(n => n < 0) removes leading negatives |
Use Skip when you know how many elements to drop. Use SkipWhile when the number depends on the data and can only be determined at runtime.
A practical example is skipping a variable-length header:
string[] lines = { "BEGIN", "BEGIN", "DATA", "value1", "value2" }; var data = lines.SkipWhile(line => line == "BEGIN"); // data: DATA, value1, value2
Skip(2) would also work here, but only because the header length happens to be fixed. If the header length varies, SkipWhile adapts without knowing the count in advance.
The Indexed Overload
The second overload of SkipWhile passes the zero-based index of each element to the predicate:
public static IEnumerable<TSource> SkipWhile<TSource>( this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
The index is the position of the element in the source sequence, not in the skipped portion. This overload is useful when the decision depends on position rather than value alone.
int[] values = { 5, 4, 3, 2, 1 }; var result = values.SkipWhile((value, index) => value > index); // index 0: 5 > 0 → true, skip // index 1: 4 > 1 → true, skip // index 2: 3 > 2 → true, skip // index 3: 2 > 3 → false, stop // result: 2, 1
A realistic use is skipping elements whose value exceeds a position-dependent threshold, such as discarding the first few readings in a series until the values stabilize relative to their position.
Deferred Execution and Runtime Behavior
SkipWhile uses deferred execution. The query does not run when you call SkipWhile; it runs when you enumerate the result. Each enumeration re-evaluates the predicate from the beginning of the source.
int[] numbers = { 1, 2, 3 }; var query = numbers.SkipWhile(n => n < 3); numbers[0] = 10; Console.WriteLine(string.Join(", ", query)); // Output: 10, 2, 3
Because the source array was modified before enumeration, the predicate sees 10 first, fails immediately, and the query returns the whole sequence. If you need a snapshot of the result, materialize it with ToList() or ToArray().
The predicate is evaluated at most once per element, and only until the first failure. For a sequence of length n, SkipWhile evaluates the predicate at most n times, and typically far fewer because it stops early.
Common Pitfalls
One recurring bug is assuming SkipWhile filters the whole sequence. As shown earlier, trailing elements that match the predicate are included because evaluation stops at the first failure.
Another pitfall is relying on side effects inside the predicate. The predicate may be called multiple times across enumerations, and the number of calls depends on where the first failure occurs. A predicate with side effects produces different behavior on each enumeration.
A third pitfall is using SkipWhile on an infinite or very large sequence without a guaranteed failure point. If the predicate never returns false, enumeration never terminates. Ensure the sequence has a point where the condition fails, or bound the sequence with Take before applying SkipWhile.
Performance and Memory Considerations
SkipWhile itself is an iterator method. It allocates a small state machine when the query is enumerated, but it does not buffer the entire sequence. Elements are consumed one at a time from the source, and skipped elements are discarded without being stored.
The cost of SkipWhile is dominated by two factors:
- The cost of the predicate itself.
- The number of elements evaluated before the first failure.
If the predicate is expensive and the failing element is deep in the sequence, SkipWhile pays the predicate cost for every element up to that point. There is no way to skip elements without evaluating the predicate, because the operator cannot know where the run ends otherwise.
When the source is a List<T> or an array, SkipWhile iterates through the leading elements directly. When the source is itself a lazily computed sequence, SkipWhile composes with it, and each enumeration re-runs the entire chain. Materializing an intermediate result with ToList() can avoid repeated work when the same query is enumerated multiple times.
Practical Use Cases
SkipWhile fits scenarios where a sequence has a leading region that should be discarded based on a condition:
- Skipping log lines before a marker such as
"ERROR"or a timestamp threshold. - Skipping leading whitespace or comment lines in a parsed file.
- Skipping preamble bytes in a binary stream until a header signature appears.
- Skipping leading invalid measurements until the first valid reading.
In each case, the condition is data-dependent, and the number of skipped elements is unknown until the predicate fails. That is exactly the situation SkipWhile was designed for.