ATeDe
ATeDe

Reputation: 11

Rotating List(Of T)

The question is how to present nicely working code of DispList and RotateList functions using LINQ eventually Lambda. Thank you in advance

Class CalcProc
    Property Year As Integer
    Property Type As String
    Property Count As Integer
    Property Total As Integer
    Property Proc As Decimal
End Class
Sub CreateListOfT()
    Dim List As New List(Of CalcProc)

    List.Add(New CalcProc With {.Year = 2013, .Type = "A", .Count = 12, .Total = 20, .Proc = calcP(.Count, .Total)})
    List.Add(New CalcProc With {.Year = 2013, .Type = "B", .Count = 22, .Total = 30, .Proc = calcP(.Count, .Total)})
    List.Add(New CalcProc With {.Year = 2014, .Type = "C", .Count = 32, .Total = 40, .Proc = calcP(.Count, .Total)})
    List.Add(New CalcProc With {.Year = 2015, .Type = "D", .Count = 42, .Total = 50, .Proc = calcP(.Count, .Total)})
    List.Add(New CalcProc With {.Year = 2016, .Type = "E", .Count = 52, .Total = 60, .Proc = calcP(.Count, .Total)})
    DispListOfT(List)
    RotateListOfT(List)

End Sub
Private Function DispListOfT(Of T)(ByVal dList As IList(Of T))

    If Not dList.Any Then
        MsgBox("NOTHING to display!", vbCritical, MethodBase.GetCurrentMethod.Name())
        End
    End If

    Dim fields() = dList.First.GetType.GetProperties
    Dim ListColumns = (From x In fields Select x.Name).ToList()
    Console.WriteLine(String.Join(",", ListColumns.ToArray))

    Dim sb As New Text.StringBuilder
    For Each item In dList
        sb.Length = 0
        For Each field In fields
            Dim p = item.GetType.GetProperty(field.Name)
            sb.Append(p.GetValue(item, Nothing)) : sb.Append(",")
        Next
        Console.WriteLine(Left(sb.ToString, sb.Length - 1))
    Next

End Function

Public Function RotateListOfT(Of T)(ByVal dList As IList(Of T))

    Dim sa As New Text.StringBuilder
    Dim fields() = dList.First.GetType.GetProperties
    Dim dCount As Integer
    Dim Modulo As Integer = dList.Count \ 4
    For i As Integer = 0 To Modulo - 1
        For Each field In fields
            sa.Append(field.Name)
            sa.Append(vbTab)
            For Each item In dList
                If dCount Mod Modulo = i Then
                    Dim p = item.GetType.GetProperty(field.Name)
                    sa.Append(p.GetValue(item, Nothing))
                    sa.Append(vbTab)
                End If
                dCount += 1
            Next
            Console.WriteLine(sa.ToString)
            sa.Length = 0
        Next
    Next
End Function
Function calcP(a As Integer, b As Integer) As Decimal
    Return Round(a * 100.0 / b, 2)
End Function

List(Of T)

Year, Type, Count, Total, Proc
2013,A,12,20,60
2013,B,22,30,73.33
2014,C,32,40,80
2015,D,42,50,84
2016,E,52,60,86.67

Rotated List(Of T)

Year    2013    2013    2014    2015    2016
Type    A       B       C       D       E
Count   12      22      32      42      52
Total   20      30      40      50      60
Proc    60      73.33   80      84      86.67

Upvotes: 1

Views: 124

Answers (1)

NetMage
NetMage

Reputation: 26907

Here is my re-write of your Subs. While you could replace the remaining For Each loops with List.ForEach, there is no advantage to doing so, and I think it is less clear. LINQ is really best for transforming sequences into results, which you then use for I/O.

Since you know the parameter dList is a IList(Of T), you know that every member will of a T with the same properties, so no reason to retrieve the properties for each item.

Finally I used lambda syntax for the LINQ as it lends itself to being nested in other function calls.

Public Sub DispListOfT(Of T)(ByVal dList As IList(Of T))
    If Not dList.Any Then
        'MsgBox("NOTHING to display!", vbCritical, MethodBase.GetCurrentMethod.Name())
        Return
    End If

    Dim props = dList.First.GetType.GetProperties
    Console.WriteLine(String.Join(",", props.Select(Function(p) p.Name)))

    For Each item In dList
        Console.WriteLine(String.Join(",", props.Select(Function(p) p.GetValue(item))))
    Next
End Sub

Public Sub RotateListOfT(Of T)(ByVal dList As IList(Of T))
    For Each prop In dList.First.GetType.GetProperties
        Console.Write(prop.Name)
        Console.Write(vbTab)
        Console.WriteLine(String.Join(vbTab, dList.Select(Function(item) prop.GetValue(item))))
    Next
End Sub

If I were writing a program to do this, I would probably separate rotation of the List from outputting the rotated List. Also, it should be very unusual to use Reflection to get property names from types, you should already know the types you are dealing with and add the output methods to the class for the type, unless your program is dealing with lots of lists of different classes for some reason.

Upvotes: 2

Related Questions