отримати ключ словника за значенням


361

Як отримати ключ словника за значенням у C #?

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

Я хочу щось подібне:

getByValueKey(string value);

getByValueKey("one")повинно бути повернення "1".

Який найкращий спосіб зробити це? Може бути, HashTable, сортовані списки?


9
Точний дублікат: stackoverflow.com/questions/255341
Гейб

я читав цю статтю раніше, але відповідь знайди.
loviji

5
Так, але там ви отримуєте прийняту відповідь від Skeet .
ruffin

7
Тут прийнята відповідь кардинально краща за все на повторне запитання. Але це питання старіше; можливо, лямбда-виразів не існувало, коли Джон відповів.
Сет Баттін

5
Повторно відкриваючи це питання, оскільки інше спеціально стосується .Net 2.0, тоді як це не робить і має кращу відповідь на поточну версію фрейму .Net.
Рейчел

Відповіді:


645

Значення необов'язково повинні бути унікальними, тому вам потрібно зробити пошук. Ви можете зробити щось подібне:

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

Якщо значення унікальні і вставляються рідше, ніж читаються, то створіть зворотний словник, де значення - це ключі, а ключі - значення.


3
@loviji: Майте на увазі, що в циклічному рішенні, якщо значення трапляється в кінці словника, для його знаходження доведеться перейти всі інші значення. Якщо у вас є кількість записів, це сповільнить вашу програму.
Зак Джонсон

2
@Zach Johnson: Дякую. я погоджуюсь з тобою. і ваша відповідь мені теж подобається. але в моєму словнику 8-10 записів. і вони не додаються динамічно. і я думаю, використовуючи цю відповідь - не погане рішення
loviji

4
Я щось тут пропускаю? Код вище повертає значення, а не ключове. Не буде типів.FirstOrDefault (x => x.Value == "one"). Ключ буде більш підходящим?
floele

19
Попереджуючи всіх, прийнята відповідь, коли вона стоїть з правками, викине виняток, якщо FirstOrDefault не знайде відповідності та спробує отримати доступ до "Key" на нульовому об'єкті.
Джим Ярбро

11
@JimYarbro: оскільки KeyValuePair<Tkey,Tvalue>це структура, тому тип значення, вона ніколи не може бути null. FirstOrDefaultповерне екземпляр, коли всі поля ініціалізовані зі своїм значенням за замовчуванням (наприклад, nullдля рядків або 0 для ints). Таким чином, ви не отримаєте винятку. Але ви також не знаєте, чи знайшли ви значення, тому ця відповідь не охоплює випадку, коли значення не існує.
Тім Шмелтер

26

Ви можете це зробити:

  1. Переглядаючи всі слова KeyValuePair<TKey, TValue>в словнику (що буде значним хітом продуктивності, якщо у вас є кілька записів у словнику)
  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 та в інших обмежених середовищах.


Додавання цього методу як розширення приємніше :-)
Хаїм Фрідман

-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 ("один") шукає ваш новий Список, шукаючи "один", і повертає індекс, який би відповідав індексу пари Ключ / Значення в словнику.

Цей метод не стосується клавіш словника, він просто повертає індекс значення, яке ви шукаєте.

Майте на увазі, що у вашому словнику може бути більше одного "одного" значення. І саме тому немає методу "отримати ключ".


-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 років до вашого). Для створення цих двох списків вам не потрібно прорисувати властивості ключів і значень (ToList Linq зробить це за вас). Крім того, якщо ви збираєтесь використовувати IndexOf, ви могли б уникнути дзвінка на ContainsValue (таким чином, уникнути 2 циклів, хоча всі елементи для одного завдання).
Маріано Дезанзе

2
Виконання цієї пропозиції просто жахливе. Ви також можете створити загальний клас із двома словниками. Один з яких містить Key1 і Key2, а другий, який містить Key2 і Key1. Таким чином ви можете отримати будь-який ключ без ... ну ... все, що запропонувала ваша відповідь.
Krythic

-12

У мене дуже простий спосіб це зробити. Це вийшло ідеально для мене.

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
Вибачте, але ваша відповідь не відповідає на запитання. Питання стосувалося того, як знайти ключ за значенням, у вашій відповіді показано стандарт: пошук значення за ключем
Бриз

1
Прочитайте запитання спочатку, наступного разу
Tommix

4
І саме це, панове, саме тому ми читаємо питання, перш ніж розміщувати відповідь.
Критичний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.