Linq Zip came to mind in order to map the items in pairs and then apply the predicate.
By skipping the first item in the list and then Zipping the original list against the skipped list will produce item pairs that can then be passed to the provided predicate.
static class MyClass {
public static IEnumerable<T> WherePrevious<T>(this IEnumerable<T> source, Func<T, T, bool> predicate) {
var other = source.Skip(1);
var blank = default(T);
return source.Zip(other, (first, second) => {
return predicate(first, second) ? second : blank;
}).Where(_ => !object.Equals(_, blank));
}
}
The default value of the item was used as a flag in order to filter out the pairs that did not satisfy the predicate.
The following test was used to demonstrate the example given in the original question.
[TestClass]
public class FilterTest {
[TestMethod]
public void WherePreviousTest() {
//Arrange
var numbers = new[] { 1, 5, 8, 7, 12, 8, 5 };
var expected = new[] { 5, 8, 12 };
//Act
var actual = numbers.WherePrevious((first, second) => second > first).ToArray();
//Assert
actual.ShouldAllBeEquivalentTo(expected);
}
}
The body of the created extension method could have been done on its own, but creating the extension method allowed for a cleaner approach.
While I would say it is easier to read IMO, technically you will be enumerating the list more than once, which makes this not as efficient as your one pass through.