1

need to create a dictionary where keys are list(Of String) or array of string

i found this link C# List as Dictionary key

but i need an help to understand how to do it with list(of String)

Class test
    Sub TryTest()
        myDict.Add(New List(Of String) From {"Huey", "Dewey"}, actions.Sleeping)
        myDict.Add(New List(Of String) From {"Dewey", "Louie"}, actions.Playing)

        Dim newList As New List(Of String) From {"Dewey", "Louie"}
        If myDict.ContainsKey(newList) Then
            MsgBox("myDict contains the list of String, as Key Value")
        Else
            MsgBox("myDict don't contains the list of String, as Key Value")
        End If
    End Sub

    Dim myDict As New Dictionary(Of List(Of String), actions)

End Class

Enum actions
    Sleeping
    Eating
    Studying
    Playing
End Enum

I expected that the dictionary output that contains the key.

P.S. Since c# it's close to vb.net, and on the net there are lot's of c#/vb.net translators that translate easily, please, also c# help are appreciated.


UPDATE (after Jeppe Stig Nielsen helps, i tried to implement a class that inherits EqualityComparer, but it doesn't work... maybe i mistake something in syntax... do someone know what's wrong in my approach?


Class test
    Sub TryTest()
        myDict.Add(New List(Of String) From {"Huey", "Dewey"}, actions.Sleeping)
        myDict.Add(New List(Of String) From {"Dewey", "Louie"}, actions.Playing)

        Dim newList As New List(Of String) From {"Dewey", "Louie"}
        If myDict.ContainsKey(newList) Then
            MsgBox("myDict contains the list of String, as Key Value")
        Else
            MsgBox("myDict don't contains the list of String, as Key Value")
        End If

        Try
            myDict.Add(newList, actions.Eating)
        Catch ex As Exception
            MsgBox("myDict don't allow me to add an already present List (Of String), as Key Value")
        End Try

    End Sub

    Dim myDict As New Dictionary(Of List(Of String), actions)(New ListComparer)

End Class

Enum actions
    Sleeping
    Eating
    Studying
    Playing
End Enum

NotInheritable Class ListComparer
    Inherits EqualityComparer(Of List(Of String))

    Public Overrides Function Equals(ByVal x As List(Of String), ByVal y As List(Of String)) As Boolean
        Return StructuralComparisons.StructuralEqualityComparer.Equals(x, y)
    End Function

    Public Overrides Function GetHashCode(ByVal x As List(Of String)) As Integer
        Return StructuralComparisons.StructuralEqualityComparer.GetHashCode(x)
    End Function
End Class
Marcello
  • 438
  • 5
  • 21
  • 2
    The issue there is that you are using reference type objects as the keys and the default comparison is reference equality, i.e. you need to test for the exact same `List`, not another `List` containing the same `Strings`. If you want to do the second way then you need to use the constructor that takes an `IEqualityComparer(Of T)`, where `T` is `List(Of String)`, that will compare two `Lists` to determine whether they are "equal" in that way. – jmcilhinney Feb 02 '19 at 07:57
  • Start by reading [the documentation for that constructor](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.-ctor?view=netframework-4.7.2) and go from there. – jmcilhinney Feb 02 '19 at 07:57
  • Are you sure you need a Dictionary for this? It looks like you'ld need a custom class that implements a custom `IComparer.Compare` method and then use a `List(Of Class)` instead of a Dictionary. The Enumerator should be part of the class, of course. The value selected through a public Property. – Jimi Feb 02 '19 at 09:12
  • Or, the class could generate a hash of its string values. Or both. – Jimi Feb 02 '19 at 09:19
  • Might be helpful: https://stackoverflow.com/questions/1171812/multi-key-dictionary-in-c – ZiggZagg Feb 02 '19 at 14:37

2 Answers2

2

With

using System.Collections;
using System.Collections.Generic;

you can create a class like

sealed class ListComparer : EqualityComparer<List<string>>
{
  public override bool Equals(List<string> x, List<string> y)
    => StructuralComparisons.StructuralEqualityComparer.Equals(x, y);

  public override int GetHashCode(List<string> x)
    => StructuralComparisons.StructuralEqualityComparer.GetHashCode(x);
}

and then use it as comparer for the Dictionary<,>

myDict = new Dictionary<List<string>, Actions>(new ListComparer());

The ListComparer class could also be generic, sealed class ListComparer<T> : EqualityComparer<List<T>>.


EDIT:

After comments, I realize that StructuralEqualityComparer does not work for List<string> (which I had thought)! It works for string[] and for stuff like Tuple<string, string, ...>. So the above class needs to be changed into:

sealed class ListComparer : EqualityComparer<List<string>>
{
  public override bool Equals(List<string> x, List<string> y)
    => StructuralComparisons.StructuralEqualityComparer.Equals(x?.ToArray(), y?.ToArray());

  public override int GetHashCode(List<string> x)
    => StructuralComparisons.StructuralEqualityComparer.GetHashCode(x?.ToArray());
}

My original attempt was wrong!

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
  • thanks, i tried but it doesn't work... maybe i mistake something on syntax... Could you have an help? – Marcello Feb 07 '19 at 10:40
  • 1
    Thanks for your help, but there is a problem. The override method GetHashCode will return a different hashcode if the list(Of String) object it's different, EVEN IF the strings are the same. So when i implement it on a Dictionary, and i just Add the same key already present, the dictionary add another duplicate of key. Because the hashCode is different, even if the strings are same. – Marcello Feb 18 '19 at 12:15
  • Thank you very much! You were right; my code did not work. Please see my edit above. – Jeppe Stig Nielsen Feb 18 '19 at 15:58
  • work great thanks. Tried also to understand 'why' and an help comes from this older answer: https://stackoverflow.com/a/2913255/6366781, and for someone it's interest on how to compare unordered list, here an help: https://stackoverflow.com/a/3790621/6366781 – Marcello Feb 20 '19 at 10:56
  • You can remove the question marks, i.e. change `?.` into just `.` in three places, if you know your comparer will never be used with a `null` list. In our use that is safe, because `Dictionary<,>` will stop you from using `null` as a key even before the equality comparer comes into play. So go ahead and remove the `?`. If the comparer class is used more generally, a `List` reference may be `null` and it is better to be safe with `null`. – Jeppe Stig Nielsen Feb 20 '19 at 12:17
  • i was imaging that the ? was referred to a null value, but because i'm not so skilled with c#, and i tried to translate your code with the 2 most knowen translator: http://converter.telerik.com/ and https://www.carlosag.net/tools/codetranslator/ , they both don't recognised syntax, i imagined was a mistake. Thank you for your precious help – Marcello Feb 22 '19 at 14:42
  • And I do not remember VB.NET well. Maybe the translators can cope with the longer (equivalent) syntax `x != null ? x.ToArray() : null` then? One translation is `If(x IsNot Nothing, x.ToArray(), Nothing)`. According to [this answer](https://stackoverflow.com/a/45470684) you __can__ use `?.` in VB.NET under some circumstances. – Jeppe Stig Nielsen Feb 22 '19 at 15:02
0

problem is you comparing 2 object. you need to compare values in it. i dont know vb.net my code is c# (LINQ). do it like this.

var myDic = new Dictionary<List<string>, Actions>();
myDic.Add(new List<string>() { "Huey", "Dewey" }, Actions.Sleeping);
myDic.Add(new List<string>() { "Dewey", "Louie" }, Actions.Playing);
var newList = new List<string>() { "Dewey", "Louie" };
if (myDic.Keys.Any(key =>
{
    if (key.Count != newList.Count) return false;

    var same = true;
    for (int i = 0; i < newList.Count; i++)
    {
        if (key[i] != newList[i]) same = false;
    }
    return same;
}))
    MsgBox("myDict contains the list of String, as Key Value");
else
    MsgBox("myDict don't contains the list of String, as Key Value")
DemiDev
  • 31
  • 6