按值获取字典键


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 2010年

5
是的,但是您可以从Skeet那里获得公认的答案
鲁芬2014年

7
在这里,被接受的答案比在重复问题上的所有答案都强得多。但是这个问题比较老。乔恩回答时,也许lambda表达式不存在。
塞斯·巴丁

5
由于另一个问题专门针对.Net 2.0,因此重新打开了这个问题,而这个问题没有解决,并且对于.Net框架的当前版本有更好的答案。
雷切尔2014年

Answers:


645

值不一定必须是唯一的,因此您必须进行查找。您可以执行以下操作:

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

如果值是唯一的,并且插入频率比读取的频率低,请创建一个反向字典,其中值是键,而键是值。


3
@loviji:请记住,在循环解决方案中,如果该值恰好在字典的末尾,则必须遍历所有其他值才能找到它。如果您有很多条目,这将降低程序速度。
扎克·约翰逊

2
@扎克·约翰逊:谢谢。我同意你的看法。我也喜欢你的回答。但在我的字典中有8-10个条目。并且不会动态添加它们。而且我认为,使用此答案还不错。
loviji 2010年

4
我在这里想念什么吗?上面的代码返回值,而不是键。不会键入。FirstOrDefault(x => x.Value ==“ one”)。Key更合适吗?
floele

19
对所有人发出警告,如果FirstOrDefault未找到匹配项并尝试访问null对象上的“键”,则接受的答案与修改一样将引发异常。
吉姆·雅布罗

11
@JimYarbro:由于KeyValuePair<Tkey,Tvalue>是一个结构,所以是一个值类型,所以永远不会是nullFirstOrDefault将返回一个实例,其中所有字段均使用其默认值初始化(例如,null对于字符串或对于整数,则为0)。这样您就不会例外。但是您也不知道是否找到了一个值,因此此答案并不涵盖该值不存在的情况。
蒂姆·施密特

26

您可以这样做:

  1. 通过遍历KeyValuePair<TKey, TValue>字典中的所有数字(如果您在字典中有许多条目,这将对性能产生很大的影响)
  2. 使用两个字典,一个用于值到键的映射,另一个用于键到值的映射(这将占用内存的两倍)。

如果不考虑性能,则使用方法1;如果不考虑内存,则使用方法2。

同样,所有键都必须是唯一的,但是值不必是唯一的。您可能有多个具有指定值的键。

有什么原因不能扭转键值关系?


1
为了以编程方式创建逆字典,我们仍然需要使用方法1,对吗?
凯尔·德莱尼

如果这是常见情况,那么我也建议您进行交换(关于您的最后一个问题)。
Bonez024,18年

3

我处于无法使用Linq绑定的情况,必须显式扩展lambda。它产生了一个简单的函数:

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”,并返回与字典中键/值对的索引匹配的Index。

此方法不关心字典键,它只返回您要查找的值的索引。

请记住,词典中可能有多个“一个”值。这就是没有“获取密钥”方法的原因。


-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太多的代码性能将比Kimi最高答案(发布于您的问世6年之前)差。您不需要foreach键和值属性来创建这两个列表(Linq的ToList将为您完成此操作)。此外,如果您要使用IndexOf,则可以避免调用ContainsValue(因此,尽管同一任务的所有元素都避免了2个循环)。
马里亚诺·德桑兹

2
这个建议的表现很糟糕。您也可以使用两个Dictionary创建一个通用类。其中一个保存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
抱歉,您的答案不符合问题。问题是关于如何通过值找到键,您的答案显示了标准:通过键找到值
Breeze 2015年

1
第一次阅读问题,下一次
Tommix

4
女士们,先生们,这就是为什么我们在发布答案之前先阅读问题。
Krythic
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.