I am trying to achieve function that filters out some entry and take minimal value mapped by concrete function, and either return minimal value judged by it or get null when all elements are filtered out:
public readonly record struct PdfVerticalPos(float Raw_LessIsBelow)
{
}
public readonly record struct AxisAlignedRenderingBox
{
public AxisAlignedRenderingBox(PdfVerticalPos upMost, PdfVerticalPos downMost, float leftMostRaw, float rightMostRaw)
{
this.UpMost = upMost;
this.DownMost = downMost;
this.LeftMost = leftMostRaw;
this.RightMost = rightMostRaw;
}
}
private static string? findTheBottomByCondition(IEnumerable<(AxisAlignedRenderingBox, string)> detectedStrings,
float leftMostMin, float leftMostMax, Func<(AxisAlignedRenderingBox, string), bool> selectingPredicate)
{
return detectedStrings
.Where(selectingPredicate)
.Where(tuple => leftMostMin <= tuple.Item1.LeftMost && tuple.Item1.LeftMost <= leftMostMax)
.MinBy(tuple => tuple.Item1.UpMost.Raw_LessIsBelow).Item2;
}
internal void someCallSite() {
var detectedStrings = whatever();
var found = findTheBottomByCondition(detectedStrings, 60, 65,
tuple => tuple.Item1.UpMost.Raw_LessIsBelow >= baseY);
// do whatever
}
However, when calling MinBy, it throws a InvalidOperationException. While I inspect the cause, I found that documentation said "it throws InvalidOperationException if TSource is a primitive type and the source sequence is empty." Not sure what it want to mean by "primitive type" since the Tuples are clearly not primitive type as opposed to integers.
So I have rewrite the findTheBottomByCondition as follows:
private static string? findTheBottomByCondition(IEnumerable<(AxisAlignedRenderingBox, string)> detectedStrings, float leftMostMin, float leftMostMax, Func<(AxisAlignedRenderingBox, string), bool> selectingPredicate) {
var valueTuples = detectedStrings.ToList();
var a = valueTuples
.Where(selectingPredicate)
.Count(tuple => leftMostMin <= tuple.Item1.LeftMost && tuple.Item1.LeftMost <= leftMostMax);
if (a > 0)
{
return valueTuples
.Where(selectingPredicate)
.Where(tuple => leftMostMin <= tuple.Item1.LeftMost && tuple.Item1.LeftMost <= leftMostMax)
.MinBy(tuple => tuple.Item1.UpMost.Raw_LessIsBelow).Item2;
}
else
{
return null;
}
}
(the code is garbage, sorry)
While it’s correctly working, but not-so-elegant to only avoiding the Exception.
I want it to return null if it’s not found regardless of if it is "primitive type", any ideas? I think I could achieve it by catch-ing InvalidOperationException, but I don’t like that way, nor current mostly-copy-pasted and poor implementation.
>Solution :
The documentation ought to say that MinBy throws InvalidOperationException if TSource is a non-nullable type, not just a primitive type.
For MinBy to return null instead of throwing InvalidOperationException, the source sequence must support null values. You can use the Cast extension method to convert the tuples to nullable tuples:
.Where(...)
.Cast<(AxisAlignedRenderingBox, string)?>()
.MinBy(...)
Or you can define an extension method that infers the correct type:
public static class EnumerableExtensions
{
public static IEnumerable<TSource?> AsNullable<TSource>(this IEnumerable<TSource> source)
where TSource : struct
{
return source.Cast<TSource?>();
}
}
And call it like so:
.Where(...)
.AsNullable()
.MinBy(...)
Because the argument to MinBy is now a nullable tuple instead of just a tuple, you’ll have to use the Value property:
.MinBy(tuple => tuple!.Value.Item1.UpMost.Raw_LessIsBelow)
And finally, since MinBy can now return null, you’ll want to use the null-conditional ?. operator before accessing the resulting tuple’s Item2 property.