In LINQ, how do I modify an existing LINQ extension method to add a "By" selector, i.e. Add Func <T> TResult to Function Signature?
I am very curious to learn how to modify an existing LINQ function to add Func<T> TResultfunctions to the signature, i.e. allow her to use the selector as in (o => o.CustomField).
For example, in C # I can use .IsDistinct()to check if a list of integers is great. I can also use .IsDistinctBy(o => o.SomeField)to check if the integers in the field are o.SomeFielddifferent. I find that backstage .IsDistinctBy(...)has something like a function attached to itFunc<T> TResult
My question is this: what is the technique for taking the existing LINQ extension function and transforming it so that it can have a parameter (o => o.SomeField)?
Here is an example.
This extension function checks if the list grows monotonously (i.e. the values never decrease, 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 "By", I add a parameter Func<T> TResult. But how do I change the function body to make it a choice (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 similar to the following, which lists a given IEnumerable<T>one only once. Listing can have side effects, and callers usually expect a single pass, if 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;
}
}
enumerable.IsIncreasingMonotonicallyBy(x => x.MyProperty) .
public static bool IsIncreasingMonotonicallyBy<T, TKey>(
this IEnumerable<T> _this,
Func<T, TKey> keySelector)
where TKey : IComparable<TKey>
{
return _this.Select(keySelector).IsIncreasingMonotonically();
}
, :
IEnumeration
public static bool MyIsIncreasingMonotonicallyBy<T, TResult>( this IEnumerable<T> list, Func<T, TResult> selector) where TResult : IComparable<TResult> { var enumerable = list as IList<T> ?? list.ToList(); return enumerable.Zip( enumerable.Skip(1), (a, b) => selector(a).CompareTo(selector(b)) <= 0 ).All(b => b); }
P.S. , "this", , .
:
:
IEnumerable<string> names = GetNames();
foreach (var name in names) Console.WriteLine("Found " + name);
var allNames = new StringBuilder();
foreach (var name in names) allNames.Append(name + " ");
, GetNames() IEnumerable, , foreach. GetNames() , , .
This problem can be easily fixed - just force the enumeration at the initialization point of the variable, converting the sequence to a list (or you can make an array). Both array and list types implement the IEnumerable interface.
// the existing LINQ operator that you want to modify:
public static … Foo<T>(this IEnumerable<T> xs, …)
{
…
}
// the extended ("modified") operator:
public static … FooBy<T, U>(this IEnumerable<T> xs, Func<T, U> func)
{
return xs.Select(func).Foo(…);
} // --^^^^^^^^^^^^^-------
// there you go!