clarkcone
clarkcone

Reputation: 13

New dictionary based on existing dictionary yields unexpected results

I'm looking to take some data from an existing dictionary object and create a new dictionary object with that data reorganized based on key values.

In our hypothetical example, I've got 7 Users (User1... User7). Their year of employment populates the key of the original dictionary and the usernames associated with that key are stored in the value as a list of strings (i.e. User1 was hired in 2018, User4 in 2019, etc.)

I then want to walk through that original dictionary and populate a new dictionary. Everyone is considered a "User" and should be added to the value associated with the "Users" key. Then, hires from 2018 should be added to the list associated with the "Alpha" key. Everything else should go to the list associated with the "Beta" key.

Sub Main(args As String())
    'create dictionary
    Dim origDictionary As New Dictionary(Of String, List(Of String))
    'create entries in the original dictionary
    origDictionary.Add("2018", New List(Of String) From {"User1", "User2", "User3"})
    origDictionary.Add("2019", New List(Of String) From {"User4", "User5"})
    origDictionary.Add("2020", New List(Of String) From {"User6", "User7"})
    'convert that dictionary based on our qualifiers
    Dim newDictionary As New Dictionary(Of String, List(Of String))
    For Each pair As KeyValuePair(Of String, List(Of String)) In origDictionary
        'every user goes into the user group
        appendToDict("Users", pair.Value, newDictionary)
        'users are also sorted into more specific groups
        Select Case pair.Key
            Case "2018"
                appendToDict("Alpha", pair.Value, newDictionary)
            Case Else
                appendToDict("Beta", pair.Value, newDictionary)
        End Select
    Next
End Sub

Private Sub appendToDict(key As String, val As List(Of String), dict As Dictionary(Of String, List(Of String)))
    If dict.ContainsKey(key) Then
        dict(key).AddRange(val)
    Else
        dict.Add(key, val)
    End If
End Sub

The expected result should be:
Users -- (User1, ..., User7)
Alpha -- (User1, User2, User3)
Beta -- (User4, User5, User6, User7)

However, the result I'm getting is:
Users -- (User1, ..., User7) -- Correct
Alpha -- (User1, ..., User7) -- Not correct
Beta -- (User4, User5, User6, User7) -- Correct

Interestingly, when inspecting the local vars (in VS Debug), the "origDictionary" is also somehow being modified. Looking back at "origDictionary" AFTER "newDictionary" has been generated shows different values than what was originally populated.

What am I missing? Is it a ByVal vs. ByRef thing?

(This problem has been generalized. No real data or proprietary code has been used)

Upvotes: 1

Views: 72

Answers (1)

jmcilhinney
jmcilhinney

Reputation: 54417

I just tested my theory and I was right. Change this:

dict.Add(key, val)

to this:

dict.Add(key, val.ToList())

If you step through your Main method in the debugger and watch the values of origDictionary("2018").Count and origDictionary("2019").Count using your code and mine, you should be able to get an idea of what the issue is. The code you have is adding the same List objects from the existing Dictionary to the new one in the Else block, so your call to AddRange will affect those existing Lists. My code creates a new List and adds that to the new Dictionary, so the call to AddRange will only affect that new List, not the existing one.

Upvotes: 1

Related Questions