I am very curious to know how to modify an existing LINQ function to add Func<T> TResult
to the function signature, i.e. allow it to use a selector as in (o => o.CustomField)
.
For example, in C#, I can use .IsDistinct()
to check if a list of integers are distinct. I can also use .IsDistinctBy(o => o.SomeField)
to check if the integers in field o.SomeField
are distinct. I believe that, behind the scenes, .IsDistinctBy(...)
has something like the function signature Func<T> TResult
appended to it?
My question is this: what is the technique for taking an existing LINQ extension function, and converting it so it can have a parameter (o => o.SomeField)
?
Here is an example.
This extension function checks to see if a list is increasing monotonically (i.e. values are never decreasing, as in 1,1,2,3,4,5,5):
main()
{
var MyList = new List<int>() {1,1,2,3,4,5,5};
DebugAssert(MyList.MyIsIncreasingMonotonically() == true);
}
public static bool MyIsIncreasingMonotonically<T>(this List<T> list) where T : IComparable
{
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0).All(b => b);
}
If I want to add a "By", I add a parameter Func<T> TResult
. But how do I modify the body of the function to make it select by (o => o.SomeField)
?
main()
{
DebugAssert(MyList.MyIsIncreasingMonotonicallyBy(o => o.CustomField) == true);
}
public static bool MyIsIncreasingMonotonicallyBy<T>(this List<T> list, Func<T> TResult) where T : IComparable
{
// Question: How do I modify this function to make it
// select by o => o.CustomField?
return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0).All(b => b);
}
Consider an implementation like the following, which enumerates the given IEnumerable<T>
only once. Enumerating can have side-effects, and callers typically expect a single pass-through if that's possible.
public static bool IsIncreasingMonotonically<T>(
this IEnumerable<T> _this)
where T : IComparable<T>
{
using (var e = _this.GetEnumerator())
{
if (!e.MoveNext())
return true;
T prev = e.Current;
while (e.MoveNext())
{
if (prev.CompareTo(e.Current) > 0)
return false;
prev = e.Current;
}
return true;
}
}
Your enumerable.IsIncreasingMonotonicallyBy(x => x.MyProperty)
overload that you describe can now be written as follows.
public static bool IsIncreasingMonotonicallyBy<T, TKey>(
this IEnumerable<T> _this,
Func<T, TKey> keySelector)
where TKey : IComparable<TKey>
{
return _this.Select(keySelector).IsIncreasingMonotonically();
}
Just apply the Func
to a and b:
public static bool MyIsIncreasingMonotonicallyBy<T, TResult>(this IEnumerable<T> list, Func<T, TResult> selector)
where TResult : IComparable<TResult>
{
return list.Zip(list.Skip(1), (a, b) => selector(a).CompareTo(selector(b)) <= 0).All(b => b);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With