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


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.