Britchie
Britchie

Reputation: 311

Using Linq for Object Dataset Processing

I have a collection (IList(Of Sample)) of the following class:

Public Class Sample
    Public sampleNum As String
    Public volume As Integer
    Public initial As Single
    Public final As Single
End Class

This collection is filled from a regex that gets passed over a file.

What I want to do is use Linq to generate a collection of these for each unique samplenum using the following conditions.

For each samplenum:

  1. Have the highest volume where the final is greater then one
    • If the sample has multiple records for this volume then pick the one with the the highest final
  2. If the previous step leaves us with no records pick the record with the highest final ignoring volume

I am extremely new to Linq and just can't get my head around this. For now I have solved this using for each's and temporary lists but I am interested in how this would be handled using pure Linq.

Sample Data:

samplenum | volume | initial | final
1         | 50     | 8.47    | 6.87
1         | 300    | 8.93    | 3.15
2         | 5      | 8.28    | 6.48
2         | 10     | 8.18    | 5.63
2         | 5      | 8.33    | 6.63
2         | 10     | 8.26    | 5.58
3         | 1      | 8.31    | 0.75
3         | 5      | 8.19    | 0.03
4         | 50     | 8.28    | 6.55
4         | 300    | 7.19    | 0.03

Upvotes: 0

Views: 258

Answers (2)

Pelto
Pelto

Reputation: 76

This should hopefully solve your problems:

    Dim source As IEnumerable(Of Sample)

    ' Get the data... 

    Dim processed = source _
                    .GroupBy(Function(s) s.sampleNum) _
                    .Select(Function(s) Process(s))

    Dim array = processed.ToArray()
    Console.ReadLine()

The Process function:

Private Function Process(ByVal sequence As IEnumerable(Of Sample)) As Sample 
    Dim filtered = (
        From s In sequence
        Where s.final > 1
        Order By
            s.volume Descending,
            s.final Descending
        )

    ' If we don't have any elements after the filtering, 
    ' return the one with the highest final.
    ' Otherwise, return the first element.
    If Not filtered.Any() Then
        Return (From s In sequence Order By s.final Descending).FirstOrDefault()
    Else
        Return filtered.First()
    End If

End Function

Upvotes: 1

Stuart Blackler
Stuart Blackler

Reputation: 3772

Try this. I haven't tried it but it should do what you want. There is probs a better way of doing this:

    ' For each sample number in the list
    For Each el In (From p In lst Select p.sampleNum).Distinct()
        ' can cause odd results in some cases so always put the foreach var into another var
        Dim var As String = el
        ' get another "list" but for this sample num
        Dim res As IEnumerable(Of Sample) = lst.Where(Function(p) p.volume > 1 AndAlso p.sampleNum = var)
        Dim sam As Sample ' the result
        If Not res Is Nothing And res.Any() Then
            ' we have a result, so get the first result where the 
            sam = res.Where(Function(p) p.volume = res.Max(Function(x) x.volume)).First()
        Else
            ' we have no results, so resort back to the normal list, for this sample number
            sam = lst.Where(Function(p) p.sampleNum = var AndAlso p.volume = lst.Max(Function(x) x.volume)).First()
        End If
        '
        ' do what ever with the sample here
        '
    Next

Upvotes: 1

Related Questions