none
VB.NET Remove all duplicate values from List(of t) LINQ RRS feed

  • Question

  • Hello,

    I am looking for a clean solution to remove all duplicates from a List(of t) collection.

    For example

    Class Person
    
    	Public Property FirstName As String
    	Public Property LastName As String
    	Public Property Gender As GenderEnum
    	Public Property Age As Integer
    
    	Public Sub New(Byval FirstName As String, Byval LastName As String, Byval Gender As GenderEnum, Byval Age As Integer)
    		Me.FirstName = FirstName
    		Me.LastName = LastName
    		Me.Gender = Gender
    		Me.Age = Age
    	End Sub
    
    End Class
    
    Public Sub TestSub()
    
    	Dim List as New List(of Person)
    	List.ADD(New Person("John", "Do", GenderEnum.Male, 22)
    	List.ADD(New Person("Barry", "White", GenderEnum.Male, 21)
    	List.ADD(New Person("Billy", "Johnson", GenderEnum.Male, 23)
    	List.ADD(New Person("Kate", "Smith", GenderEnum.Female, 21)
    	List.ADD(New Person("Jenny", "Winston", GenderEnum.Female, 25)
    
    End Sub

    As result I would like to get a list where ALL persons where the age is duplicate to be removed from the list. For this example "Barry" and "Kate" should not be included in the list.

    If something is not clear please, let me know.

    Thanks!


    Wednesday, May 2, 2018 1:58 PM

Answers

  • Something like this

        Public Sub TestSub()
    
            Dim List As New List(Of Person)
            List.Add(New Person("John", "Do", GenderEnum.male, 22))
            List.Add(New Person("Barry", "White", GenderEnum.male, 21))
            List.Add(New Person("Billy", "Johnson", GenderEnum.male, 23))
            List.Add(New Person("Kate", "Smith", GenderEnum.female, 21))
            List.Add(New Person("Jenny", "Winston", GenderEnum.female, 25))
    
            Dim ie As IEnumerable(Of Person)
            ie = From p In List
                 Let ct = (From pa In List Where pa.Age = p.Age Select pa).Count
                 Where ct = 1
                 Select p
    
            'List = ie.ToList
        End Sub
    


    "Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it."

    - from former MSDN User JohnWein

    SerialPort Info

    Multics - An OS ahead of its time.

    Wednesday, May 2, 2018 2:27 PM

All replies

  • Something like this

        Public Sub TestSub()
    
            Dim List As New List(Of Person)
            List.Add(New Person("John", "Do", GenderEnum.male, 22))
            List.Add(New Person("Barry", "White", GenderEnum.male, 21))
            List.Add(New Person("Billy", "Johnson", GenderEnum.male, 23))
            List.Add(New Person("Kate", "Smith", GenderEnum.female, 21))
            List.Add(New Person("Jenny", "Winston", GenderEnum.female, 25))
    
            Dim ie As IEnumerable(Of Person)
            ie = From p In List
                 Let ct = (From pa In List Where pa.Age = p.Age Select pa).Count
                 Where ct = 1
                 Select p
    
            'List = ie.ToList
        End Sub
    


    "Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it."

    - from former MSDN User JohnWein

    SerialPort Info

    Multics - An OS ahead of its time.

    Wednesday, May 2, 2018 2:27 PM
  • Should you not first make your code running. "My" is a telling that it is a property from the My namespace. 

    The correct keyword is in the way you use "Me".



    Success
    Cor

    Wednesday, May 2, 2018 2:53 PM
  • Somehow contributors in this forum love the old C# enumerable interface.

    If I take Johns, code, make the code from MechanicalEngineer runable then I get this code. Which does what is asked.

    The result is in the IE list. 

    Module Module1
        Sub Main()
            Dim xList As New List(Of Person)
            xList.Add(New Person("John", "Do", 22))
            xList.Add(New Person("Barry", "White", 21))
            xList.Add(New Person("Billy", "Johnson", 23))
            xList.Add(New Person("Kate", "Smith", 21))
            xList.Add(New Person("Jenny", "Winston", 25))
            Dim ie As New List(Of Person)
            ie = (From p In xList
                  Let ct = (From pa In xList Where pa.Age = p.Age Select pa).Count
                  Where ct = 1
                  Select p).ToList
        End Sub
    End Module
    Class Person
        Public Property FirstName As String
        Public Property LastName As String
        Public Property Age As Integer
        Public Sub New(FirstName As String, LastName As String, Age As Integer)
            Me.FirstName = FirstName
            Me.LastName = LastName
            Me.Age = Age
        End Sub
    End Class

      

    Success
    Cor


    Wednesday, May 2, 2018 3:01 PM
  • LINQ is for selecting so you'll need to first get the items to remove and then remove them.  You can use something like the following:

    Public Class Form1
        Public Sub TestSub()
            Dim List As New List(Of Person)
            List.Add(New Person("John", "Do", GenderEnum.Male, 22))
            List.Add(New Person("Barry", "White", GenderEnum.Male, 21))
            List.Add(New Person("Billy", "Johnson", GenderEnum.Male, 23))
            List.Add(New Person("Kate", "Smith", GenderEnum.Female, 21))
            List.Add(New Person("Jenny", "Winston", GenderEnum.Female, 25))
    
            Dim dups = (From l In List Group By l.Age Into Count Where Count > 1 Select Age).ToArray
            List.RemoveAll(Function(p)
                               Return dups.Contains(p.Age)
                           End Function)
        End Sub
    End Class
    
    Public Enum GenderEnum
        Male
        Female
    End Enum
    
    Class Person
        Public Property FirstName As String
        Public Property LastName As String
        Public Property Gender As GenderEnum
        Public Property Age As Integer
        Public Sub New(ByVal FirstName As String, ByVal LastName As String, ByVal Gender As GenderEnum, ByVal Age As Integer)
            Me.FirstName = FirstName
            Me.LastName = LastName
            Me.Gender = Gender
            Me.Age = Age
        End Sub


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    • Proposed as answer by dbasnett Wednesday, May 2, 2018 5:01 PM
    Wednesday, May 2, 2018 3:01 PM
    Moderator
  • Or this. :)

    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Wednesday, May 2, 2018 3:02 PM
    Moderator
  • Reed

    I would have expected from you as well a gender Neutral

    Somehow I had something against the used Enum

    But I don't know how important that is in your state.

    :-)


    Success
    Cor

    Wednesday, May 2, 2018 3:10 PM
  • Reed

    I would have expected from you as well a gender Neutral

    Somehow I had something against the used Enum

    But I don't know how important that is in your state.

    :-)


    Success
    Cor

    I just corrected the OP's code and reused it to make the example clear.

    This is certainly not the place to have such a discussion and my personal viewpoint on the matter is irrelevant.  :)


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Wednesday, May 2, 2018 3:39 PM
    Moderator
  • Reed

    I would have expected from you as well a gender Neutral

    Somehow I had something against the used Enum

    But I don't know how important that is in your state.

    :-)


    Success
    Cor


    Based on the OP's post there were only males and females in the sample.

    "Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it."

    - from former MSDN User JohnWein

    SerialPort Info

    Multics - An OS ahead of its time.

    Wednesday, May 2, 2018 5:04 PM
  • Hello,

    Another twist is having a generic language extension method.

    Public Module Extensions
        <Runtime.CompilerServices.Extension>
        Public Function DistinctBy(Of TSource, TKey)(
            ByVal source As IEnumerable(Of TSource),
            ByVal keySelector As Func(Of TSource, TKey)) As IEnumerable(Of TSource)
    
            Dim hKeys = New HashSet(Of TKey)()
            Return source.Where(Function(element) hKeys.Add(keySelector(element)))
    
        End Function
    End Module

    Usage

    Dim distinctList As IEnumerable(Of Person) =
        List.DistinctBy(Function(item) item.Age)

    So this means we can use the same method on 

    Public Class Car
        Public Property Company As String
        Public Property Model As String
        Public Property Year As Integer
    End Class

    Same usage

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim carList As New List(Of Car) From
            {
                New Car With {.Company = "Mazda", .Model = "Miata", .Year = 2015},
                New Car With {.Company = "Ford", .Model = "Mustang", .Year = 2016},
                New Car With {.Company = "Mazda", .Model = "Miata", .Year = 2015}
            }
    
        Dim distinctList As IEnumerable(Of Car) =
            carList.DistinctBy(Function(item) item.Model)
    
        For Each c As Car In distinctList
            Console.WriteLine($"{c.Company}, {c.Model}, {c.Year}")
        Next
    End Sub
    Of course the above may be overkill or not for you.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites


    Wednesday, May 2, 2018 5:15 PM
    Moderator
  • If this time you are interested in regular loops and do not need the original list, then try this fragment too:

    Dim i = 0
    Do While i < List.Count - 1
       Dim f = False
       Dim j = i + 1
       Do
          j = List.FindIndex(j, Function(p) p.Age = List(i).Age)
          If j > 0 Then
             f = True
             List.RemoveAt(j)
          End If
       Loop Until j < 0
       If f Then List.RemoveAt(i) Else i += 1
    Loop




    • Edited by Viorel_MVP Wednesday, May 2, 2018 6:01 PM
    Wednesday, May 2, 2018 5:58 PM
  • Hi mechanicalEngineer,

    You can also try the following code:

    Class Person
    
        Public Property FirstName As String
        Public Property LastName As String
        Public Property Gender As GenderEnum
        Public Property Age As Integer
    
        Public Sub New(ByVal FirstName As String, ByVal LastName As String, ByVal Gender As GenderEnum, ByVal Age As Integer)
            Me.FirstName = FirstName
            Me.LastName = LastName
            Me.Gender = Gender
            Me.Age = Age
        End Sub
        Enum GenderEnum
            Male
            Female
        End Enum
    
    End Class
    Public Class Form2
     Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    
            List.Add(New Person("John", "Do", GenderEnum.Male, 22))
            List.Add(New Person("Barry", "White", GenderEnum.Male, 21))
            List.Add(New Person("Billy", "Johnson", GenderEnum.Male, 23))
            List.Add(New Person("Kate", "Smith", GenderEnum.Female, 21))
            List.Add(New Person("Jenny", "Winston", GenderEnum.Female, 25))
            Dim duplicatevalue = List.GroupBy(Function(x) x.Age).Where(Function(x) x.Count > 1).SelectMany(Function(x) x).ToList()
            Dim setToRemove As New HashSet(Of Person)(duplicatevalue)
            List.RemoveAll(Function(x) setToRemove.Contains(x))
    
        End Sub
    End Class

    Best Regards,

    Cherry


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, May 3, 2018 6:16 AM
    Moderator
  • Thanks for this - it works perfectly. When there is a need to select distinct using multiple keys, concatenating them and passing as TKey works well.
    Tuesday, December 11, 2018 7:13 AM