Die Linq.Max(selector As Func(T, TProp As IComparable)) - Aggregat-Funktion geht mir auf die Nerven.
Angenommen eine Punkte-Wolke, und ich brauche den höchsten Punkt:
Ausgabe: "Max(pt.Y): 92"
Na toll! Der Y-Wert des höchsten Punktes.
Ich wollte aber den höchsten Punkt selbst haben: {X=47,Y=92}
Also habichmir was ausgedacht, was ähnlich wie bei Order by nicht den Vergleichswert selbst zurückgibt, sondern den Träger des Vergleichswertes. Logischerweise heißt diese Aggregat-Funktion also MaxBy():
Spoiler anzeigen
Jo, diese Extensions lassen sich identisch wie olle Linq.Max() verwenden:
Ausgabe
Im beiliegenden Sample habich auch noch was mit LinqToDataset verbrochen, nämlich es geht um eine PatientenAkten-Verwaltung, und da will man eine Ansicht des jeweils letzten AusleihVorgangs (History) pro PatientenAkte haben:
Also selektiert wird von jeder PatientenAkte ihre Ausleih-History mit dem spätesten AusgabeDatum (MaxBy(hist.OutAt))
Angenommen eine Punkte-Wolke, und ich brauche den höchsten Punkt:
Ausgabe: "Max(pt.Y): 92"
Na toll! Der Y-Wert des höchsten Punktes.
Ich wollte aber den höchsten Punkt selbst haben: {X=47,Y=92}
Also habichmir was ausgedacht, was ähnlich wie bei Order by nicht den Vergleichswert selbst zurückgibt, sondern den Träger des Vergleichswertes. Logischerweise heißt diese Aggregat-Funktion also MaxBy():
VB.NET-Quellcode
- Public Structure MinMax(Of T, T2 As IComparable)
- Public Min, Max As T
- Public MinVal, MaxVal As T2
- Private _Selector As Func(Of T, T2)
- Public ReadOnly HasValue As Boolean
- Public Sub New(ByVal first As T, ByVal selector As Func(Of T, T2))
- _Selector = selector
- Me.Min = first : Me.Max = first
- MinVal = _Selector(first) : MaxVal = MinVal
- HasValue = True
- End Sub
- Public Sub Check(ByVal itm As T)
- Dim val = _Selector(itm)
- If val.CompareTo(MinVal) < 0 Then
- Min = itm
- MinVal = val
- ElseIf val.CompareTo(MaxVal) > 0 Then
- Max = itm
- MaxVal = val
- End If
- End Sub
- End Structure
- <Extension()> _
- Public Function Extremum(Of T, T2 As IComparable)( _
- ByVal items As IEnumerable(Of T), ByVal selector As Func(Of T, T2)) As MinMax(Of T, T2)
- With items.GetEnumerator
- If Not .MoveNext Then .Dispose() : Return Nothing
- Extremum = New MinMax(Of T, T2)(.Current, selector)
- While .MoveNext : Extremum.Check(.Current) : End While
- .Dispose()
- End With
- End Function
- <Extension()> _
- Public Function MaxBy(Of T, T2 As IComparable)( _
- ByVal items As IEnumerable(Of T), ByVal selector As Func(Of T, T2)) As T
- Return items.Extremum(selector).Max
- End Function
- <Extension()> _
- Public Function MinBy(Of T, T2 As IComparable)( _
- ByVal items As IEnumerable(Of T), ByVal selector As Func(Of T, T2)) As T
- Return items.Extremum(selector).Min
- End Function
VB.NET-Quellcode
- Module modMain
- Public Sub main()
- Dim rnd = New Random
- Dim points = Enumerable.Range(0, 50).Select(Function(i) New Point(rnd.Next(-100, 100), rnd.Next(-100, 100))).ToList
- points.ForEach(Sub(pt) Console.Write("{0} ", pt)) 'alle ausgeben
- Write()
- 'aufruf als Extension
- Write("Max(pt.Y):", points.Max(Function(pt) pt.Y))
- Write("MaxBy(pt.Y):", points.MaxBy(Function(pt) pt.Y))
- Write("Extremum(pt.Y):", points.Extremum(Function(pt) pt.Y))
- 'aufruf in Linq-Syntax
- Write("Min(pt.Y):", Aggregate pt In points Into Min(pt.Y))
- Write("MinBy(pt.Y):", Aggregate pt In points Into MinBy(pt.Y))
- Console.ReadLine()
- End Sub
- Private Sub Write(ByVal ParamArray msg() As Object)
- Console.WriteLine(String.Concat(msg.Select(Function(m) m.ToString & " ")))
- End Sub
- End Module
{X=82,Y=-64} {X=-84,Y=-82} {X=-92,Y=47} {X=-30,Y=57} {X=-54,Y=46} {X=-96,Y=-63} {X=37,Y=-33} {X=-59,Y=-65} {X=28,Y=-4} {X=-42,Y=7} {X=23,Y=62} {X=80,Y=-8} {X=33,Y=-53} {X=-60,Y=-81} {X=-89,Y=-73} {X=-38,Y=-99} {X=68,Y=-87} {X=4,Y=-12} {X=36,Y=-10} {X=20,Y=7} {X=-11,Y=91} {X=68,Y=-84} {X=-33,Y=80} {X=37,Y=-26} {X=37,Y=-2} {X=-20,Y=-30} {X=96,Y=-17} {X=96,Y=-80} {X=-45,Y=23} {X=-16,Y=60} {X=7,Y=-50} {X=45,Y=-81} {X=-16,Y=-98} {X=-31,Y=23} {X=62,Y=53} {X=-1,Y=-62} {X=76,Y=50} {X=-82,Y=31} {X=93,Y=64} {X=-75,Y=53} {X=10,Y=66} {X=-19,Y=12} {X=47,Y=92} {X=-95,Y=-16} {X=-83,Y=-16} {X=-68,Y=-88} {X=-95,Y=59} {X=-100,Y=-62} {X=-54,Y=49} {X=32,Y=-35}
Max(pt.Y): 92
MaxBy(pt.Y): {X=47,Y=92}
Extremum(pt.Y): MinMax<Point>: {X=-38,Y=-99} - {X=47,Y=92}
Min(pt.Y): -99
MinBy(pt.Y): {X=-38,Y=-99}
Max(pt.Y): 92
MaxBy(pt.Y): {X=47,Y=92}
Extremum(pt.Y): MinMax<Point>: {X=-38,Y=-99} - {X=47,Y=92}
Min(pt.Y): -99
MinBy(pt.Y): {X=-38,Y=-99}
Im beiliegenden Sample habich auch noch was mit LinqToDataset verbrochen, nämlich es geht um eine PatientenAkten-Verwaltung, und da will man eine Ansicht des jeweils letzten AusleihVorgangs (History) pro PatientenAkte haben:
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()