値で辞書キーを取得する


361

C#で値によって辞書キーを取得するにはどうすればよいですか?

Dictionary<string, string> types = new Dictionary<string, string>()
{
            {"1", "one"},
            {"2", "two"},
            {"3", "three"}
};

私はこのようなものが欲しい:

getByValueKey(string value);

getByValueKey("one")返品する必要があります"1"

これを行う最良の方法は何ですか?たぶんHashTable、SortedLists?



私は前にこの記事を読みましたが、答えはそこにあります。
loviji

5
はい、しかし、そこであなたはスキートから受け入れられた答えを得ます。
ruffin 2014年

7
ここで受け入れられた回答は、重複する質問のすべてよりもはるかに優れています。しかし、その質問は古いです。ジョンが答えたとき、おそらくラムダ式は存在しませんでした。
セスバティン2014年

5
もう1つは具体的に.Net 2.0に対応しているため、この質問を再度開きますが、この質問は.Netフレームワークの現在のバージョンには対応しておらず、より適切な答えを持っています。
レイチェル

回答:


645

値は必ずしも一意である必要はないため、ルックアップを行う必要があります。あなたはこのようなことをすることができます:

var myKey = types.FirstOrDefault(x => x.Value == "one").Key;

値が一意であり、読み取りよりも挿入頻度が低い場合は、値がキーで、キーが値である逆ディクショナリを作成します。


3
@loviji:ループソリューションでは、値が辞書の最後にある場合、他のすべての値を調べてそれを見つける必要があることに注意してください。多数のエントリがある場合、これによりプログラムの速度が低下します。
ザックジョンソン

2
@ザック・ジョンソン:ありがとう。仰るとおりです。そしてあなたの答えも好きです。しかし、私の辞書では8-10エントリです。そしてそれらは動的に追加されません。そして私は、この答えを使うことは悪い解決策ではないと思います。
loviji

4
ここで何か不足していますか?上記のコードは、キーではなく値を返します。types.FirstOrDefault(x => x.Value == "one")。Keyの方が適切ではないでしょうか?
floele 2013

19
全員に警告します。FirstOrDefaultが一致を見つけられず、nullオブジェクトの "Key"にアクセスしようとすると、承認された回答は編集で待機するため、例外がスローされます。
ジムヤーブロ2017

11
@JimYarbro:KeyValuePair<Tkey,Tvalue>は構造体なので、値の型なので、決してなりませんnullFirstOrDefaultすべてのフィールドがデフォルト値(null文字列の場合やintの場合0 など)で初期化されるインスタンスを返します。したがって、例外は発生しません。ただし、値が見つかったかどうかもわからないため、この回答では、値が存在しない場合はカバーされません。
Tim Schmelter、2018

26

あなたはそれを行うことができます:

  1. KeyValuePair<TKey, TValue>ディクショナリ内のすべてのをループすることにより(ディクショナリ内に多数のエントリがある場合、かなりのパフォーマンスヒットになります)
  2. 2つのディクショナリーを使用します。1つは値からキーへのマッピング用で、もう1つはキーから値へのマッピング用です(メモリー内で2倍のスペースを使用します)。

パフォーマンスが考慮されない場合は方法1を使用し、メモリが考慮されない場合は方法2を使用します。

また、すべてのキーは一意である必要がありますが、値は一意である必要はありません。指定した値を持つキーが複数ある場合があります。

キーと値の関係を元に戻せない理由はありますか?


1
プログラムで逆辞書を作成するために、メソッド1を使用する必要がありますよね?
カイルデラニー

これが一般的な出来事である場合は、私もこのスワップをお勧めします(最後の質問に関して)。
Bonez024

3

私は、Linqバインディングが利用できず、ラムダを明示的に拡張する必要があった状況にありました。それは単純な関数をもたらしました:

public static T KeyByValue<T, W>(this Dictionary<T, W> dict, W val)
{
    T key = default;
    foreach (KeyValuePair<T, W> pair in dict)
    {
        if (EqualityComparer<W>.Default.Equals(pair.Value, val))
        {
            key = pair.Key;
            break;
        }
    }
    return key;
}

次のように呼び出します。

public static void Main()
{
    Dictionary<string, string> dict = new Dictionary<string, string>()
    {
        {"1", "one"},
        {"2", "two"},
        {"3", "three"}
    };

    string key = KeyByValue(dict, "two");       
    Console.WriteLine("Key: " + key);
}

.NET 2.0およびその他の限られた環境で動作します。


それを拡張メソッドとして追加する方がいいです:-)
Chayim Friedman

-1

おそらくこのようなもの:

foreach (var keyvaluepair in dict)
{
    if(Object.ReferenceEquals(keyvaluepair.Value, searchedObject))
    {
        //dict.Remove(keyvaluepair.Key);
        break;
    }
}

-1

私は二重参照クラスを作成しました:

/// <summary>
/// dictionary with double key lookup
/// </summary>
/// <typeparam name="T1">primary key</typeparam>
/// <typeparam name="T2">secondary key</typeparam>
/// <typeparam name="TValue">value type</typeparam>
public class cDoubleKeyDictionary<T1, T2, TValue> {
    private struct Key2ValuePair {
        internal T2 key2;
        internal TValue value;
    }
    private Dictionary<T1, Key2ValuePair> d1 = new Dictionary<T1, Key2ValuePair>();
    private Dictionary<T2, T1> d2 = new Dictionary<T2, T1>();

    /// <summary>
    /// add item
    /// not exacly like add, mote like Dictionary[] = overwriting existing values
    /// </summary>
    /// <param name="key1"></param>
    /// <param name="key2"></param>
    public void Add(T1 key1, T2 key2, TValue value) {
        lock (d1) {
            d1[key1] = new Key2ValuePair {
                key2 = key2,
                value = value,
            };
            d2[key2] = key1;
        }
    }

    /// <summary>
    /// get key2 by key1
    /// </summary>
    /// <param name="key1"></param>
    /// <param name="key2"></param>
    /// <returns></returns>
    public bool TryGetValue(T1 key1, out TValue value) {
        if (d1.TryGetValue(key1, out Key2ValuePair kvp)) {
            value = kvp.value;
            return true;
        } else {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetValue2(T2 key2, out TValue value) {
        if (d2.TryGetValue(key2, out T1 key1)) {
            return TryGetValue(key1, out value);
        } else {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetKey1(T2 key2, out T1 key1) {
        return d2.TryGetValue(key2, out key1);
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetKey2(T1 key1, out T2 key2) {
        if (d1.TryGetValue(key1, out Key2ValuePair kvp1)) {
            key2 = kvp1.key2;
            return true;
        } else {
            key2 = default;
            return false;
        }
    }

    /// <summary>
    /// remove item by key 1
    /// </summary>
    /// <param name="key1"></param>
    public void Remove(T1 key1) {
        lock (d1) {
            if (d1.TryGetValue(key1, out Key2ValuePair kvp)) {
                d1.Remove(key1);
                d2.Remove(kvp.key2);
            }
        }
    }

    /// <summary>
    /// remove item by key 2
    /// </summary>
    /// <param name="key2"></param>
    public void Remove2(T2 key2) {
        lock (d1) {
            if (d2.TryGetValue(key2, out T1 key1)) {
                d1.Remove(key1);
                d2.Remove(key2);
            }
        }
    }

    /// <summary>
    /// clear all items
    /// </summary>
    public void Clear() {
        lock (d1) {
            d1.Clear();
            d2.Clear();
        }
    }

    /// <summary>
    /// enumerator on key1, so we can replace Dictionary by cDoubleKeyDictionary
    /// </summary>
    /// <param name="key1"></param>
    /// <returns></returns>
    public TValue this[T1 key1] {
        get => d1[key1].value;
    }

    /// <summary>
    /// enumerator on key1, so we can replace Dictionary by cDoubleKeyDictionary
    /// </summary>
    /// <param name="key1"></param>
    /// <returns></returns>
    public TValue this[T1 key1, T2 key2] {
        set {
            lock (d1) {
                d1[key1] = new Key2ValuePair {
                    key2 = key2,
                    value = value,
                };
                d2[key2] = key1;
            }
        }
    }

-3
types.Values.ToList().IndexOf("one");

Values.ToList()は、ディクショナリ値をオブジェクトのリストに変換します。IndexOf( "one")は、新しいリストを検索して「one」を探し、ディクショナリ内のキー/値ペアのインデックスと一致するインデックスを返します。

このメソッドは、ディクショナリキーを気にしません。単に、探している値のインデックスを返します。

ディクショナリには複数の「1」値がある場合があることに注意してください。そして、それが「キーを取得する」メソッドがない理由です。


-4

以下のコードは、一意の値のデータが含まれている場合にのみ機能します

public string getKey(string Value)
{
    if (dictionary.ContainsValue(Value))
    {
        var ListValueData=new List<string>();
        var ListKeyData = new List<string>();

        var Values = dictionary.Values;
        var Keys = dictionary.Keys;

        foreach (var item in Values)
        {
            ListValueData.Add(item);
        }

        var ValueIndex = ListValueData.IndexOf(Value);
        foreach (var item in Keys)
        {
            ListKeyData.Add(item);
        }

        return  ListKeyData[ValueIndex];

    }
    return string.Empty;
}

3
-1 (6年前に投稿された)Kimiから最高の回答よりも最悪のパフォーマンスにはコードが多すぎます。これらの2つのリストを作成するために、KeysプロパティとValuesプロパティをforeachする必要はありません(LinqのToListがそれを行います)。さらに、IndexOfを使用する場合は、ContainsValueの呼び出しを回避できます(したがって、同じタスクのすべての要素で2つのループが回避されます)。
マリアーノデサンツェ

2
この提案のパフォーマンスはひどいものです。2つの辞書を持つジェネリッククラスを作成することもできます。1つはKey1とKey2を保持し、もう1つはKey2とKey1を保持します。このようにして、あなたの答えが示唆するすべてのものなしで、どちらのキーも取得できます。
Krythic

-11

これを行うには非常に簡単な方法があります。それは私にぴったりでした。

Dictionary<string, string> types = new Dictionary<string, string>();

types.Add("1", "one");
types.Add("2", "two");
types.Add("3", "three");

Console.WriteLine("Please type a key to show its value: ");
string rLine = Console.ReadLine();

if(types.ContainsKey(rLine))
{
    string value_For_Key = types[rLine];
    Console.WriteLine("Value for " + rLine + " is" + value_For_Key);
}

3
申し訳ありませんが、あなたの答えは質問に適合しません。質問は値によってキーを見つける方法についてでした、あなたの答えは標準を示しています:キーによって値を見つける
Breeze

1
質問を最初に読んで、次回は
2015

4
そして皆さん、これが回答を投稿する前に質問を読む理由です。
Krythic 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.