Search code examples
arraysvb.netstructure

Setting string a variable to get values from a structured array


I have a simple problem in VB.net. I want to retrieve datas from an array declared as structure using a string to visualize the variable. Below my code:

Module DataAnalisys

Dim InputData(100000) As InputDataStructure
Dim VariablesParameter(6) As VariablesParameterStructure
Dim VariablesGlobal(6) As VariablesDataStrucutre

Structure InputDataStructure
    Dim ID As Integer
    Dim FileId As Integer
    Dim ProductionDate As Date
    Dim ProductionTime As Date
    Dim Shift As Integer
    Dim IPP As Integer
    Dim BPS As Integer
    Dim SerialNumber As String
    Dim Top As Single
    Dim Bottom As Single
    Dim Right As Single
    Dim Left As Single
    Dim OffCutH As Single
    Dim OffCutV As Single
    Dim Row As Byte
    Dim Col As String
    Dim Position As String
    Dim Pack As Integer
    Dim Sheet As Integer
    Dim SheetInPack As Integer
End Structure

Structure VariablesParameterStructure
    Dim NameParameter As String
    Dim Target As Single
    Dim Tolerance As Single
    Dim LowTolerance As Single
    Dim UppTolerance As Single
End Structure

Structure VariablesDataStrucutre
    Dim NameData As String
    Dim Position As String
    Dim N As Long
    Dim Mean As Single
    Dim Difference As Single
    Dim Scrap As Single
    Dim ScrapGreat As Single
    Dim ScrapLess As Single
    Dim SeMean As Single
    Dim StDev As Single
    Dim Min As Single
    Dim Q1 As Single
    Dim Median As Single
    Dim Q3 As Single
    Dim Max As Single
End Structure

Sub RoutineAnalisysGlobal()

    For B As Byte = 0 To VariablesGlobal.Length - 1

        Dim ID As String = VariablesParameter(B).NameParameter
        Dim Target As Single = VariablesParameter(B).Target
        Dim LowTol As Single = VariablesParameter(B).LowTolerance
        Dim UppTol As Single = VariablesParameter(B).UppTolerance

        With VariablesGlobal(B)
            .NameData = ID
            .Position = "All"
            .N = InputData.Length
            .Mean = InputData.Average(Function(f) f.ID)
            .Difference = .Mean - Target
            .ScrapLess = InputData.Count(Function(f) f.ID < LowTol)
            .ScrapGreat = InputData.Count(Function(f) f.ID > UppTol)
            .Q1 = InputData.FirstQuartile(Function(F) F.ID)
            .Min = InputData.Min(Function(f) f.ID)
            .Median = InputdataMedian(Function(f) f.ID)
            .Q3 = InputData.ThirdQuartile(Function(f) f.ID)
            .Max = InputData.Max(Function(f) f.ID)
        End With
    Next

End Sub

End Module

The VariablesParameter().Name are Top, Bottom, Right, Left, OffCutH and OffCutV.

The code does not work when I'm using the part (Function(f) f.ID) as I want to use the function on different items of the structure of InputData according the for cycle. I want to display f.Top f.Bottom f.Right f.Left f.OffCutH and f.OffCutV.

Does anyone want to help me? I was looking on how convert the string ID into the variable of InputData.?????? structure.


Solution

  • I think what you're looking for is something like this:

    Dim functions As Func(Of InputDataStructure, Single)() = {Function(ids) ids.Top,
                                                              Function(ids) ids.Bottom,
                                                              Function(ids) ids.Right,
                                                              Function(ids) ids.Left,
                                                              Function(ids) ids.OffCutH,
                                                              Function(ids) ids.OffCutV}
    
    For i = 0 To VariablesGlobal.GetUpperBound(0)
        With VariablesGlobal(i)
            '...
            .Mean = InputData.Average(functions(i))
            '...
        End With
    Next
    

    By way of explanation, the Function(f) f.ID part of this line:

    .Mean = InputData.Average(Function(f) f.ID)
    

    is a lambda expression, i.e. a delegate for an anonymous method. The Average method that you're calling requires a delegate for a method that has a single argument of type T, where T matches the generic type of the IEnumerable(Of T) that you're calling it on, and returns a value of a numeric type, e.g. Integer or Single. In your case, you are calling Average on InputData, which implements IEnumerable(Of InputDataStructure). Your lambda expression has a parameter of type InputDataStructure and returns an Integer, so it works with Average. The Average method invokes that delegate for every item in the list, gets all the numeric results and then divides that by the number of items in the list to get the average.

    To make it clearer what is happening there, you could used a named method instead of a lambda expression. You could write this method:

    Private Function GetID(f As InputDataStructure) As Integer
        Return f.ID
    End Function
    

    and then change your code to this:

    .Mean = InputData.Average(AddressOf GetID)
    

    Hopefully you can identify the parts of that named method that correspond to the lambda express and the parts that are inferred.

    Now, you have a number of lines of code there that use the same lambda expression, e.g.

    .Min = InputData.Min(Function(f) f.ID)
    .Median = InputdataMedian(Function(f) f.ID)
    .Q3 = InputData.ThirdQuartile(Function(f) f.ID)
    

    for something so simple, it is customary to do it the way you have. You do have the option, though, of writing the lambda expression once, assigning it to a variable and then using that variable multiple times. You could do this:

    Dim f As Func(Of InputDataStructure, Integer) = Function(ids) ids.ID
    

    or this:

    Dim f = Function(ids As InputDataStructure) ids.ID
    

    to end up with the variable f referring to a delegate that takes an argument of type InputDataStructure and returns a value of type Integer. You can then use that variable where you previously used multiple lambda expressions:

    .Min = InputData.Min(f)
    .Median = InputdataMedian(f)
    .Q3 = InputData.ThirdQuartile(f)
    

    So, now that we determined that all you need is a delegate to a method that takes an argument of the correct type and returns the correct type, take another look at the code I posted. It first creates an array of six delegates, each of which takes an argument of type InputDataStructure and returns a value of type Single. You can then use an element of that array wherever such a delegate is expect.

    That means that you can loop over that array and pass each delegate to all the extensions methods that expect such a delegate, including Average. If you get the delegate at index 0 then you get the delegate that returns the Top property value, so Average will get the average Top value. If you use index 5 then you get the delegate that returns the OffCutV property value, so Average will get the average OffCutV value. Etc.

    For the record, I recommend using parameter names in lambdas that indicate the type. You used f but that's meaningless. The parameter type is InputDataStructure so ids is indicative of that.