получить ключ словаря по значению

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
источник
9
Точная копия: stackoverflow.com/questions/255341
Гейб
Я читал эту статью раньше, но ответ получен.
Loviji
5
Да, но там вы получите принятый ответ от Скита .
ruffin
7
Принятый ответ здесь намного лучше, чем все в дублирующем вопросе. Но этот вопрос старше; возможно, лямбда-выражения не было, когда Джон ответил.
Сет Бэттин
5
Повторное открытие этого вопроса, так как другой конкретно обращается к .Net 2.0, в то время как этот не имеет и имеет лучший ответ для текущей версии .Net Framework.
Рэйчел

Ответы:

645

Значения не обязательно должны быть уникальными, поэтому вам нужно искать. Вы можете сделать что-то вроде этого:

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

Если значения уникальны и вставляются реже, чем считываются, создайте обратный словарь, в котором значения являются ключами, а ключи являются значениями.

Kimi
источник
3
@loviji: имейте в виду, что в циклическом решении, если значение окажется в конце словаря, ему нужно будет просмотреть все остальные значения, чтобы найти его. Если у вас есть несколько записей, это замедлит вашу программу.
Зак Джонсон
2
@ Зак Джонсон: Спасибо. Я с тобой согласен. и твой ответ мне тоже нравится. но в моем словаре 8-10 записей. и они не добавляются динамически. и я думаю, что использование этого ответа не плохое решение.
Loviji
4
Я что-то здесь упускаю? Код выше возвращает значение, а не ключ. Разве не будет types.FirstOrDefault (x => x.Value == "one"). Ключ будет более подходящим?
Флоэль
19
Предупреждение для всех, принятый ответ в том виде, в каком он есть с правками, вызовет исключение, если FirstOrDefault не найдет совпадения и попытается получить доступ к «Ключу» на нулевом объекте.
Джим Ярбро
11
@JimYarbro: так KeyValuePair<Tkey,Tvalue>как это структура, поэтому тип значения никогда не может быть null. FirstOrDefaultвернет экземпляр, где все поля инициализируются со значением по умолчанию (например, nullдля строк или 0 для целых). Таким образом, вы не получите исключение. Но вы также не знаете, нашли ли вы значение, поэтому этот ответ не охватывает случай, когда значение не существует.
Тим Шмелтер
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 ("one") ищет ваш новый список в поисках "one" и возвращает индекс, который будет соответствовать индексу пары ключ / значение в словаре.

Этот метод не заботится о ключах словаря, он просто возвращает индекс значения, которое вы ищете.

Имейте в виду, что в вашем словаре может быть несколько значений «один». И это причина, по которой нет метода «получить ключ».

EricM
источник
-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 лет до вашего). Вам не нужно указывать свойства Keys и Values ​​для создания этих двух списков (ToList от Linq сделает это за вас). Кроме того, если вы собираетесь использовать IndexOf, вы могли бы избежать вызова ContainsValue (таким образом избегая двух циклов, хотя все элементы для одной и той же задачи).
Мариано Дезанце
2
Производительность этого предложения просто ужасна. Вы также можете создать общий класс с двумя словарями. Один из которых содержит Key1 и Key2, а другой - 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
Извините, но ваш ответ не отвечает на вопрос. Вопрос был о том, как найти ключ по значению, ваш ответ показывает стандарт: поиск значения по ключу
Бриз
1
Сначала прочитайте вопрос, в следующий раз
Tommix
4
И это, дамы и господа, именно поэтому мы читаем вопросы, прежде чем отправлять ответ.
Krythic