Erhalten Sie mehrere Schlüssel mit dem angegebenen Wert eines generischen Wörterbuchs?

122

Es ist einfach, den Wert eines Schlüssels aus einem generischen .NET-Wörterbuch abzurufen:

Dictionary<int, string> greek = new Dictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
string secondGreek = greek[2];  // Beta

Der Versuch, den Schlüsseln einen Wert zu geben, ist jedoch nicht so einfach, da es mehrere Schlüssel geben kann:

int[] betaKeys = greek.WhatDoIPutHere("Beta");  // expecting single 2
Dour High Arch
quelle
1
Warum ist der Rückgabetyp, int[]wenn Sie einen einzelnen Wert erwarten?
Anar Khalilov
3
@ Anar, lies meine Antwort auf Domenic; "Doppelte Werte sind unwahrscheinlich, aber nicht unmöglich".
Dour High Arch
der Schlüssel eines Wertes? Ich denke du meinst die Schlüssel
Max Hodges

Antworten:

144

Okay, hier ist die mehrfach bidirektionale Version:

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

class BiDictionary<TFirst, TSecond>
{
    IDictionary<TFirst, IList<TSecond>> firstToSecond = new Dictionary<TFirst, IList<TSecond>>();
    IDictionary<TSecond, IList<TFirst>> secondToFirst = new Dictionary<TSecond, IList<TFirst>>();

    private static IList<TFirst> EmptyFirstList = new TFirst[0];
    private static IList<TSecond> EmptySecondList = new TSecond[0];

    public void Add(TFirst first, TSecond second)
    {
        IList<TFirst> firsts;
        IList<TSecond> seconds;
        if (!firstToSecond.TryGetValue(first, out seconds))
        {
            seconds = new List<TSecond>();
            firstToSecond[first] = seconds;
        }
        if (!secondToFirst.TryGetValue(second, out firsts))
        {
            firsts = new List<TFirst>();
            secondToFirst[second] = firsts;
        }
        seconds.Add(second);
        firsts.Add(first);
    }

    // Note potential ambiguity using indexers (e.g. mapping from int to int)
    // Hence the methods as well...
    public IList<TSecond> this[TFirst first]
    {
        get { return GetByFirst(first); }
    }

    public IList<TFirst> this[TSecond second]
    {
        get { return GetBySecond(second); }
    }

    public IList<TSecond> GetByFirst(TFirst first)
    {
        IList<TSecond> list;
        if (!firstToSecond.TryGetValue(first, out list))
        {
            return EmptySecondList;
        }
        return new List<TSecond>(list); // Create a copy for sanity
    }

    public IList<TFirst> GetBySecond(TSecond second)
    {
        IList<TFirst> list;
        if (!secondToFirst.TryGetValue(second, out list))
        {
            return EmptyFirstList;
        }
        return new List<TFirst>(list); // Create a copy for sanity
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        greek.Add(5, "Beta");
        ShowEntries(greek, "Alpha");
        ShowEntries(greek, "Beta");
        ShowEntries(greek, "Gamma");
    }

    static void ShowEntries(BiDictionary<int, string> dict, string key)
    {
        IList<int> values = dict[key];
        StringBuilder builder = new StringBuilder();
        foreach (int value in values)
        {
            if (builder.Length != 0)
            {
                builder.Append(", ");
            }
            builder.Append(value);
        }
        Console.WriteLine("{0}: [{1}]", key, builder);
    }
}
Jon Skeet
quelle
2
Sollte dies nach dem, was ich in msdn gelesen habe, nicht ein BiLookup anstelle eines BiDictionary sein? Nicht, dass es wichtig ist oder so, nur neugierig, ob ich die Dinge hier richtig verstehe ...
Svish
Außerdem habe ich GetByFirst verwendet und die EmptySecondList zurückbekommen, einige Dinge hinzugefügt und dann erneut GetByFirst aufgerufen. Würde ich dann nicht eine Liste mit einigen Dingen erhalten und dann keine leere Liste?
Svish
@Svish: Nein, denn wenn Sie versuchen, der Liste etwas hinzuzufügen, wird eine Ausnahme ausgelöst (Sie können kein Array hinzufügen). Und ja, BiLookup wäre wahrscheinlich ein besserer Name.
Jon Skeet
Obwohl ich sehe, dass dies die Frage von OP beantwortet, ist dies nicht eine etwas naive Implementierung? Wäre eine realistischere Implementierung nicht Dictionary <> List <> Dictionary, damit Sie tatsächlich mit 2 verschiedenen Schlüsseln nach reichen Objekten suchen können?
Chris Marisic
@ChrisMarisic: Ich bin mir nicht sicher, was du meinst - aber so etwas habe ich ziemlich oft benutzt und brauchte nichts mehr.
Jon Skeet
74

Wie alle anderen gesagt haben, gibt es in einem Wörterbuch keine Zuordnung von Wert zu Schlüssel.

Ich habe gerade bemerkt, dass Sie eine Zuordnung von Wert zu mehreren Schlüsseln vornehmen möchten. Ich lasse diese Lösung hier für die Einzelwertversion, füge dann aber eine weitere Antwort für eine bidirektionale Zuordnung mit mehreren Einträgen hinzu.

Der normale Ansatz besteht darin, zwei Wörterbücher zu haben - eines in eine Richtung und eines in die andere. Kapseln Sie sie in eine separate Klasse und überlegen Sie, was Sie tun möchten, wenn Sie einen doppelten Schlüssel oder Wert haben (z. B. eine Ausnahme auslösen, den vorhandenen Eintrag überschreiben oder den neuen Eintrag ignorieren). Persönlich würde ich wahrscheinlich eine Ausnahme auslösen - dies erleichtert die Definition des Erfolgsverhaltens. Etwas wie das:

using System;
using System.Collections.Generic;

class BiDictionary<TFirst, TSecond>
{
    IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
    IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

    public void Add(TFirst first, TSecond second)
    {
        if (firstToSecond.ContainsKey(first) ||
            secondToFirst.ContainsKey(second))
        {
            throw new ArgumentException("Duplicate first or second");
        }
        firstToSecond.Add(first, second);
        secondToFirst.Add(second, first);
    }

    public bool TryGetByFirst(TFirst first, out TSecond second)
    {
        return firstToSecond.TryGetValue(first, out second);
    }

    public bool TryGetBySecond(TSecond second, out TFirst first)
    {
        return secondToFirst.TryGetValue(second, out first);
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        int x;
        greek.TryGetBySecond("Beta", out x);
        Console.WriteLine(x);
    }
}
Jon Skeet
quelle
1
Ich glaube nicht, dass es einen Grund gibt, es von einer konkreten Klasse abzuleiten - ich mag Vererbung nicht ohne sehr sorgfältige Überlegungen -, aber es könnte sicherlich IEnumerable usw. implementieren. Tatsächlich könnte es IDictionary <TFirst, TSecond> und IDictionary implementieren <TSecond, TFirst>.
Jon Skeet
1
(Obwohl das ziemlich seltsam wäre, wenn TFirst und TSecond gleich wären ...)
Jon Skeet
6
Tatsächlich können Sie nicht sowohl IDictionary <TFirst, TSecond> als auch IDictionary <TSecond, TFirst> gleichzeitig implementieren. .NET 4.0 lässt dies nicht zu
Sebastian
2
@nawfal: Einer der Wörterbuchaufrufe schlägt Addfehl - aber wenn es der zweite ist, haben wir das System in einen verwirrten Zustand versetzt. Meiner Meinung nach haben Sie nach der Ausnahme immer noch eine konsistente Sammlung.
Jon Skeet
1
@nawfal: Nun, ich weiß nicht, ob ich es deshalb getan habe, als ich die Antwort zum ersten Mal schrieb ... Ich vermute;)
Jon Skeet
26

Wörterbücher sollen eigentlich nicht so funktionieren, denn die Eindeutigkeit von Schlüsseln ist zwar garantiert, die Eindeutigkeit von Werten jedoch nicht. Also zB wenn du es hättest

var greek = new Dictionary<int, string> { { 1, "Alpha" }, { 2, "Alpha" } };

Was würden Sie erwarten, um zu bekommen greek.WhatDoIPutHere("Alpha")?

Daher können Sie nicht erwarten, dass so etwas in das Framework integriert wird. Sie benötigen eine eigene Methode für Ihre eigenen Zwecke - möchten Sie ein Array (oder IEnumerable<T>) zurückgeben? Möchten Sie eine Ausnahme auslösen, wenn der angegebene Wert mehrere Schlüssel enthält? Was ist, wenn es keine gibt?

Persönlich würde ich mich für eine Aufzählung entscheiden, wie folgt:

IEnumerable<TKey> KeysFromValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue val)
{
    if (dict == null)
    {
        throw new ArgumentNullException("dict");
    }
    return dict.Keys.Where(k => dict[k] == val);
}

var keys = greek.KeysFromValue("Beta");
int exceptionIfNotExactlyOne = greek.KeysFromValue("Beta").Single();
Domenic
quelle
Eine elegante Lösung, die aber in 2.0 funktionieren muss. Doppelte Werte sind unwahrscheinlich, aber nicht unmöglich. Die Rückgabe einer Sammlung ist besser.
Dour High Arch
23

Der vielleicht einfachste Weg, dies ohne Linq zu tun, besteht darin, die Paare zu durchlaufen:

int betaKey; 
foreach (KeyValuePair<int, string> pair in lookup)
{
    if (pair.Value == value)
    {
        betaKey = pair.Key; // Found
        break;
    }
}
betaKey = -1; // Not found

Wenn Sie Linq hätten, hätte es leicht so gehen können:

int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;
CMS
quelle
dour, aber du hast oben einen var-Typ?! sicherlich bist du in 3.0? siehe auch mein Update unten.
Taube
Entschuldigung, ich habe "var" verwendet, um die Eingabe zu reduzieren. Ich würde es vorziehen, keine lineare Suche durchzuführen, das Wörterbuch könnte groß sein.
Dour High Arch
2
varist eine Sprachfunktion, keine Framework-Funktion. Sie können Null-Coalescing von C # -6.0 verwenden und trotzdem auf CF-2.0 abzielen, wenn Sie dies wirklich möchten.
Binki
3

Ein Wörterbuch enthält keinen Hash der Werte, sondern nur der Schlüssel. Daher dauert jede Suche mit einem Wert mindestens linear. Am besten iterieren Sie einfach über die Elemente im Wörterbuch und verfolgen die übereinstimmenden Schlüssel oder wechseln zu einer anderen Datenstruktur. Behalten Sie möglicherweise zwei Wörterbuchzuordnungsschlüssel bei -> Wert und Wert-> List_of_keys. Wenn Sie Letzteres tun, tauschen Sie Speicher gegen Suchgeschwindigkeit. Es würde nicht viel kosten, das @ Cybis-Beispiel in eine solche Datenstruktur umzuwandeln.

Tvanfosson
quelle
3

Da ich ein vollwertiges biDirektionales Wörterbuch (und nicht nur eine Karte) haben wollte, habe ich die fehlenden Funktionen hinzugefügt, um es zu einer IDictionary-kompatiblen Klasse zu machen. Dies basiert auf der Version mit eindeutigen Schlüssel-Wert-Paaren. Hier ist die Datei, falls gewünscht (die meiste Arbeit war das XMLDoc durch):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common
{
    /// <summary>Represents a bidirectional collection of keys and values.</summary>
    /// <typeparam name="TFirst">The type of the keys in the dictionary</typeparam>
    /// <typeparam name="TSecond">The type of the values in the dictionary</typeparam>
    [System.Runtime.InteropServices.ComVisible(false)]
    [System.Diagnostics.DebuggerDisplay("Count = {Count}")]
    //[System.Diagnostics.DebuggerTypeProxy(typeof(System.Collections.Generic.Mscorlib_DictionaryDebugView<,>))]
    //[System.Reflection.DefaultMember("Item")]
    public class BiDictionary<TFirst, TSecond> : Dictionary<TFirst, TSecond>
    {
        IDictionary<TSecond, TFirst> _ValueKey = new Dictionary<TSecond, TFirst>();
        /// <summary> PropertyAccessor for Iterator over KeyValue-Relation </summary>
        public IDictionary<TFirst, TSecond> KeyValue => this;
        /// <summary> PropertyAccessor for Iterator over ValueKey-Relation </summary>
        public IDictionary<TSecond, TFirst> ValueKey => _ValueKey;

        #region Implemented members

        /// <Summary>Gets or sets the value associated with the specified key.</Summary>
        /// <param name="key">The key of the value to get or set.</param>
        /// <Returns>The value associated with the specified key. If the specified key is not found,
        ///      a get operation throws a <see cref="KeyNotFoundException"/>, and
        ///      a set operation creates a new element with the specified key.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
        /// The property is retrieved and <paramref name="key"/> does not exist in the collection.</exception>
        /// <exception cref="T:System.ArgumentException"> An element with the same key already
        /// exists in the <see cref="ValueKey"/> <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public new TSecond this[TFirst key]
        {
            get { return base[key]; }
            set { _ValueKey.Remove(base[key]); base[key] = value; _ValueKey.Add(value, key); }
        }

        /// <Summary>Gets or sets the key associated with the specified value.</Summary>
        /// <param name="val">The value of the key to get or set.</param>
        /// <Returns>The key associated with the specified value. If the specified value is not found,
        ///      a get operation throws a <see cref="KeyNotFoundException"/>, and
        ///      a set operation creates a new element with the specified value.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="val"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
        /// The property is retrieved and <paramref name="val"/> does not exist in the collection.</exception>
        /// <exception cref="T:System.ArgumentException"> An element with the same value already
        /// exists in the <see cref="KeyValue"/> <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public TFirst this[TSecond val]
        {
            get { return _ValueKey[val]; }
            set { base.Remove(_ValueKey[val]); _ValueKey[val] = value; base.Add(value, val); }
        }

        /// <Summary>Adds the specified key and value to the dictionary.</Summary>
        /// <param name="key">The key of the element to add.</param>
        /// <param name="value">The value of the element to add.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception>
        /// <exception cref="T:System.ArgumentException">An element with the same key or value already exists in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public new void Add(TFirst key, TSecond value) {
            base.Add(key, value);
            _ValueKey.Add(value, key);
        }

        /// <Summary>Removes all keys and values from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        public new void Clear() { base.Clear(); _ValueKey.Clear(); }

        /// <Summary>Determines whether the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/> contains the specified
        ///      KeyValuePair.</Summary>
        /// <param name="item">The KeyValuePair to locate in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</param>
        /// <Returns>true if the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/> contains an element with
        ///      the specified key which links to the specified value; otherwise, false.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
        public bool Contains(KeyValuePair<TFirst, TSecond> item) => base.ContainsKey(item.Key) & _ValueKey.ContainsKey(item.Value);

        /// <Summary>Removes the specified KeyValuePair from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        /// <param name="item">The KeyValuePair to remove.</param>
        /// <Returns>true if the KeyValuePair is successfully found and removed; otherwise, false. This
        ///      method returns false if <paramref name="item"/> is not found in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
        public bool Remove(KeyValuePair<TFirst, TSecond> item) => base.Remove(item.Key) & _ValueKey.Remove(item.Value);

        /// <Summary>Removes the value with the specified key from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <Returns>true if the element is successfully found and removed; otherwise, false. This
        ///      method returns false if <paramref name="key"/> is not found in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        public new bool Remove(TFirst key) => _ValueKey.Remove(base[key]) & base.Remove(key);

        /// <Summary>Gets the key associated with the specified value.</Summary>
        /// <param name="value">The value of the key to get.</param>
        /// <param name="key">When this method returns, contains the key associated with the specified value,
        ///      if the value is found; otherwise, the default value for the type of the key parameter.
        ///      This parameter is passed uninitialized.</param>
        /// <Returns>true if <see cref="ValueKey"/> contains an element with the specified value; 
        ///      otherwise, false.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="value"/> is null.</exception>
        public bool TryGetValue(TSecond value, out TFirst key) => _ValueKey.TryGetValue(value, out key);
        #endregion
    }
}
DW.com
quelle
2

überarbeitet: Okay, um eine Art Fund zu haben, würden Sie etwas anderes als ein Wörterbuch benötigen, denn wenn Sie darüber nachdenken, sind Wörterbücher Einwegschlüssel. Das heißt, die Werte sind möglicherweise nicht eindeutig

Das heißt, es sieht so aus, als würden Sie c # 3.0 verwenden, sodass Sie möglicherweise nicht auf Schleifen zurückgreifen müssen und Folgendes verwenden könnten:

var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true)  == 0 select k.Key).FirstOrDefault();
Taube
quelle
Das Wörterbuch hat nicht .FindByValue. Ich würde lieber zu einer anderen Datenstruktur wechseln, als Werte zu durchlaufen.
Dour High Arch
2

Die Wörterbuchklasse ist für diesen Fall nicht optimiert, aber wenn Sie es wirklich wollten (in C # 2.0), können Sie Folgendes tun:

public List<TKey> GetKeysFromValue<TKey, TVal>(Dictionary<TKey, TVal> dict, TVal val)
{
   List<TKey> ks = new List<TKey>();
   foreach(TKey k in dict.Keys)
   {
      if (dict[k] == val) { ks.Add(k); }
   }
   return ks;
}

Ich bevorzuge die LINQ-Lösung für Eleganz, aber dies ist der 2.0-Weg.

dbkk
quelle
1

Können Sie keine Unterklasse von Dictionary erstellen, die diese Funktionalität hat?


    public class MyDict < TKey, TValue > : Dictionary < TKey, TValue >
    {
        private Dictionary < TValue, TKey > _keys;

        public TValue this[TKey key]
        {
            get
            {
                return base[key];
            }
            set 
            { 
                base[key] = value;
                _keys[value] = key;
            }
        }

        public MyDict()
        {
            _keys = new Dictionary < TValue, TKey >();
        }

        public TKey GetKeyFromValue(TValue value)
        {
            return _keys[value];
        }
    }

EDIT: Sorry, habe den Code beim ersten Mal nicht richtig verstanden.

Cybis
quelle
Dadurch wird lediglich umgeschaltet, was ich für einen Schlüssel verwende, und es wird nur der int-Wert des Zeichenfolgenschlüssels zurückgegeben. Ich muss in beide Richtungen gehen. Und wie Domenic betont, kann ich doppelte Zeichenfolgenwerte haben.
Dour High Arch
Wenn Sie doppelte Zeichenfolgenwerte für Ihre int-Schlüssel haben können, was erwarten Sie dann, wenn Sie nach Zeichenfolgen suchen? Ein Listenobjekt der entsprechenden Ints?
Cybis
1

Die hier vorgeschlagene "einfache" bidirektionale Wörterbuchlösung ist komplex und möglicherweise schwer zu verstehen, zu warten oder zu erweitern. Auch die ursprüngliche Frage fragte nach "dem Schlüssel für einen Wert", aber es könnte eindeutig mehrere Schlüssel geben (ich habe die Frage seitdem bearbeitet). Der ganze Ansatz ist ziemlich verdächtig.

Softwareänderungen. Das Schreiben von Code, der einfach zu warten ist, sollte Vorrang vor anderen "cleveren" komplexen Problemumgehungen haben. Der Weg, um Schlüssel von Werten in einem Wörterbuch zurückzubekommen, ist eine Schleife. Ein Wörterbuch ist nicht bidirektional ausgelegt.

Max Hodges
quelle
Oder möglicherweise ein zweites Wörterbuch, das jeden Wert seinen Schlüsseln zuordnet.
DavidRR
@DavidRR only-Schlüssel müssen eindeutig sein, damit der zweite Wörterbuchansatz nicht wirklich funktioniert. Sie können das Wörterbuch aber auch einfach durchlaufen, um die Schlüssel für einen Wert zu erhalten.
Max Hodges
Wenn das Problem ein Wörterbuch erfordert, das mehrere intWerte pro stringSchlüssel unterstützt, kann das Wörterbuch folgendermaßen definiert werden : Dictionary<string, List<int>>.
DavidRR
Wie kann man das bidirektional machen, ohne es zu iterieren?
Max Hodges
In Bezug auf die Frage des OP, ein Standard Dictionaryist nicht eine bidirektionale Fähigkeit bieten. Wenn Sie also nur einen Standard haben Dictionaryund die Schlüssel finden möchten, die einem bestimmten Wert zugeordnet sind, müssen Sie tatsächlich iterieren! Bei "großen" Wörterbüchern kann das Iterieren jedoch zu einer schlechten Leistung führen. Beachten Sie, dass die Antwort , die ich selbst angeboten habe, auf Iteration basiert (über LINQ). Wenn sich Ihre Initiale Dictionarynicht weiter ändert, können Sie Dictionaryeinmal eine Umkehrung erstellen, um die Rückwärtssuche zu beschleunigen.
DavidRR
1

Verwenden Sie LINQ , um eine umgekehrte Dictionary<K, V>Suche durchzuführen. Beachten Sie jedoch, dass die Werte in Ihren Dictionary<K, V>Werten möglicherweise nicht unterschiedlich sind.

Demonstration:

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

class ReverseDictionaryLookupDemo
{
    static void Main()
    {
        var dict = new Dictionary<int, string>();
        dict.Add(4, "Four");
        dict.Add(5, "Five");
        dict.Add(1, "One");
        dict.Add(11, "One"); // duplicate!
        dict.Add(3, "Three");
        dict.Add(2, "Two");
        dict.Add(44, "Four"); // duplicate!

        Console.WriteLine("\n== Enumerating Distinct Values ==");
        foreach (string value in dict.Values.Distinct())
        {
            string valueString =
                String.Join(", ", GetKeysFromValue(dict, value));

            Console.WriteLine("{0} => [{1}]", value, valueString);
        }
    }

    static List<int> GetKeysFromValue(Dictionary<int, string> dict, string value)
    {
        // Use LINQ to do a reverse dictionary lookup.
        // Returns a 'List<T>' to account for the possibility
        // of duplicate values.
        return
            (from item in dict
             where item.Value.Equals(value)
             select item.Key).ToList();
    }
}

Erwartete Ausgabe:

== Enumerating Distinct Values ==
Four => [4, 44]
Five => [5]
One => [1, 11]
Three => [3]
Two => [2]
DavidRR
quelle
1
Das Problem, das ich dabei sehe, ist, dass Sie jedes Element im Wörterbuch überprüfen, um die umgekehrte Richtung zu erhalten. Eine O (n) -Suchzeit macht den Zweck der Verwendung eines Wörterbuchs zunichte; es sollte O (1) sein.
Stephen
@stephen - Einverstanden. Wie andere bereits betont haben, wäre ein separates Wörterbuch für die Werte oder ein bidirektionales Wörterbuch geeignet , wenn die Leistung von größter Bedeutung ist . Wenn die Notwendigkeit einer Wertsuche jedoch selten ist und die Leistung dabei akzeptabel ist, ist der hier skizzierte Ansatz möglicherweise erwägenswert. Die Verwendung von LINQ in meiner Antwort ist jedoch nicht mit dem Wunsch des OP nach einer Lösung vereinbar, die für die Verwendung mit .NET 2.0 geeignet ist. (Obwohl eine .NET 2.0-Einschränkung im Jahr 2014 wahrscheinlich weniger wahrscheinlich ist.)
DavidRR
1
Dictionary<string, string> dic = new Dictionary<string, string>();
dic["A"] = "Ahmed";
dic["B"] = "Boys";

foreach (string mk in dic.Keys)
{
    if(dic[mk] == "Ahmed")
    {
        Console.WriteLine("The key that contains \"Ahmed\" is " + mk);
    }
}
Loay
quelle
1
Vielen Dank für die Beantwortung! Während ein Code-Snippet die Frage beantworten könnte, ist es immer noch großartig, einige zusätzliche Informationen hinzuzufügen, wie zum Beispiel erklären usw.
j0k
0

Als eine Wendung der akzeptierten Antwort ( https://stackoverflow.com/a/255638/986160 ) unter der Annahme, dass die Schlüssel mit Signle-Werten im Wörterbuch verknüpft werden. Ähnlich wie ( https://stackoverflow.com/a/255630/986160 ), aber etwas eleganter. Die Neuheit besteht darin, dass die konsumierende Klasse als Aufzählungsalternative verwendet werden kann (aber auch für Zeichenfolgen) und dass das Wörterbuch IEnumerable implementiert.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

Und als konsumierende Klasse könnten Sie haben

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}
Michail Michailidis
quelle
1
Dies entspricht im Grunde einer Antwort, die vor sechs Jahren veröffentlicht wurde, und wie damals erwähnt, sind Schlüssel keinen einzelnen Werten zugeordnet. Jeder Schlüssel kann mehrere Werte haben.
Dour High Arch
Nun, ich weiß, aber meine Version implementiert IEnumerable und ist eleganter. Außerdem stellt das Beispiel für die Verbrauchsklasse die BiDictionary-Klasse auf eine andere Ebene der Benutzerfreundlichkeit - es löst das Problem statischer Aufzählungen von Zeichenfolgen und IDs, die nicht von C # bereitgestellt werden. Ich habe mich auch darauf bezogen, wenn Sie meine Antwort gelesen haben!
Michail Michailidis
0

Dann die Lösung für Laien

Eine ähnliche Funktion wie die folgende könnte geschrieben werden, um ein solches Wörterbuch zu erstellen:

    public Dictionary<TValue, TKey> Invert(Dictionary<TKey, TValue> dict) {
    Dictionary<TValue, TKey> ret = new Dictionary<TValue, TKey>();
    foreach (var kvp in dict) {ret[kvp.value] = kvp.key;} return ret; }
beppe9000
quelle