alwaysVBNET alwaysVBNET - 21 days ago 9
Vb.net Question

LINQ Select Distinct objects from a List(Of T) on multiple properties does not work

My goal is to select the objects based on three

properties
:
CreatedBy, GrantedTo, PatientID
.
The following is the data I'm having in my
List(Of Access)


enter image description here

So from the above example I should be getting two objects only:

1) CreatedBy:1, GrantedTo: 65, PatientID: 48
2) CreatedBy:1, GrantedTo: 66, PatientID: 48


but I'm getting eight.

Here is my buggy code:

Dim distinctList As List(Of Access) = t.GroupBy(Function(p) New With {p.GrantedTo, p.PatientID, p.CreatedBy}).[Select](Function(g) g.First()).ToList()


I believe the code should select the first from each unique group of objects.

Any ideas?

Answer

You have a big flaw in your logic:

So from the above example I should be getting two objects only:
1) CreatedBy:1, GrantedTo: 65, PatientID: 48
2) CreatedBy:1, GrantedTo: 66, PatientID: 48
[...]
I believe the code should select the first from each unique group of objects.

Yes, you can get your data grouped by CreatedBy, GrantedTo and PatientID.
But what you get back cannot be (reasonable) objects of type Access and it will not be the first from each group.

Why? Because when you want to select all data from your object - and thus AccessID, PermissionID etc. - what values should those attributes have?

In your example 1):
Should AccesID be 238 or 240?
Should PermissionID be 15 or 18?
...
I guess you got the point.

So what you actually should do is to select the grouped data only, not as Access objects but either as an anonymous type or an defined type.

Anonymous version:

Dim distinct = From acc In access
               Group By g = New With {Key .CreatedBy = acc.CreatedBy,
                                      Key .GrantedTo = acc.GrantedTo,
                                      Key .PatientID = acc.PatientID} Into Group

(The Key keyword is mandatory here!)

Now if you want to pass these distinct values to another object/function you can pass them as single paramaeters:

For Each value In distinct
     foo.bar(value.g.CreatedBy, value.g.GrantedTo, value.g.PatientID)
Next

Or you create an own small class which contains only the three properties. (I leave this point out since I´m running out of time but it should be straight forward).

EDIT
Try the following (typed blindly and untested):

Dim distinct = (From acc In access
               Group By g = New With {Key .CreatedBy = acc.CreatedBy,
                                      Key .GrantedTo = acc.GrantedTo,
                                      Key .PatientID = acc.PatientID} Into Group
               Select New Access With {.CreatedBy = g.CreatedBy, 
                                      .GrantedTo = g.GrantedTo, 
                                      .PateintID = g.PatientID}).ToList()

This should give you a List(Of Access) with new Access objects with only the three properties set.

Comments