按值获取字典键

Posted

技术标签:

【中文标题】按值获取字典键【英文标题】:Get dictionary key by value 【发布时间】:2011-01-27 11:49:36 【问题描述】:

如何在 C# 中?

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

    "1", "one",
    "2", "two",
    "3", "three"
;

我想要这样的东西:

getByValueKey(string value);

getByValueKey("one") 必须返回 "1"

最好的方法是什么?也许是 HashTable 或 SortedLists?

【问题讨论】:

我以前读过这篇文章,但回答到那里。 是的,但是你会得到an accepted answer from Skeet。 这里接受的答案比重复问题中的所有内容都要好得多。但是这个问题更老了。乔恩回答时可能不存在 lambda 表达式。 重新打开这个问题,因为另一个问题专门针对 .Net 2.0,而这个问题没有,并且对当前版本的 .Net 框架有更好的答案。 这能回答你的问题吗? Getting multiple keys of specified value of a generic Dictionary? 【参考方案1】:

值不一定必须是唯一的,因此您必须进行查找。你可以这样做:

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

如果值是唯一的并且插入的频率低于读取的频率,则创建一个逆字典,其中值是键,键是值。

【讨论】:

@loviji:请记住,在循环解决方案中,如果该值恰好位于字典的末尾,则必须遍历所有其他值才能找到它。如果您有许多条目,这会减慢您的程序。 @Zach Johnson:谢谢。我同意你的看法。你的回答我也喜欢。但在我的字典中有 8-10 个条目。并且它们不是动态添加的。我认为,使用这个答案不错的解决方案。 我在这里遗漏了什么吗?上面的代码返回值,而不是键。 types.FirstOrDefault(x => x.Value == "one").Key 不是更合适吗? 警告所有人,如果 FirstOrDefault 找不到匹配项并尝试访问 null 对象上的“Key”,则接受的编辑将引发异常。 @JimYarbro:因为KeyValuePair&lt;Tkey,Tvalue&gt; 是一个结构,所以是一个值类型,它永远不可能是nullFirstOrDefault 将返回一个实例,其中所有字段都使用其默认值初始化(如 null 用于字符串或 0 用于整数)。所以你不会得到例外。但是你也不知道你是否找到了一个值,所以这个答案没有涵盖该值不存在的情况。【参考方案2】:

你可以这样做:

    通过遍历字典中的所有KeyValuePair&lt;TKey, TValue&gt;(如果字典中有许多条目,这将对性能造成相当大的影响) 使用两个字典,一个用于值到键的映射,一个用于键到值的映射(这将占用两倍的内存空间)。

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

此外,所有键都必须是唯一的,但值不需要是唯一的。您可能有多个具有指定值的键。

【讨论】:

为了以编程方式创建逆字典,我们仍然需要使用方法 1,对吧? 如果这种情况很常见,那么我也会推荐这种交换方式(关于你的最后一个问题)。【参考方案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 和其他受限环境。

【讨论】:

添加它作为扩展方法更好:-)【参考方案4】:
public static string GetKeyFromValue(string valueVar)

    foreach (string keyVar in dictionaryVar.Keys)
    
        if (dictionaryVar[keyVar] == valueVar)
        
            return keyVar;
        
    
    return null;

其他人可能有更有效的答案,但我个人觉得这更直观,而且对我来说很有效。

【讨论】:

这些值可能不是唯一的。那会发生什么?它是否总是返回相同的密钥(例如,在新进程中)?【参考方案5】:

我创建了一个双重查找类:

/// <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;
            
        
    

【讨论】:

【参考方案6】:

可能是这样的:

foreach (var keyvaluepair in dict)

    if(Object.ReferenceEquals(keyvaluepair.Value, searchedObject))
    
        //dict.Remove(keyvaluepair.Key);
        break;
    

【讨论】:

【参考方案7】:
types.Values.ToList().IndexOf("one");

Values.ToList() 将您的字典值转换为对象列表。 IndexOf("one") 在新列表中搜索“one”并返回与字典中键/值对的索引匹配的索引。

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

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

【讨论】:

【参考方案8】:

以下代码仅在包含唯一值数据时才有效:

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;

【讨论】:

-1 代码过多,性能会比top answer from Kimi(比你早6年发布)更差。您不需要 foreach Keys 和 Values 属性来创建这 2 个列表(Linq 的 ToList 将为您执行此操作)。此外,如果您打算使用 IndexOf,则可以避免调用 ContainsValue(从而避免通过同一任务的所有元素进行 2 次循环)。 这个建议的表现太糟糕了。您不妨创建一个具有两个字典的泛型类。其中一个持有 Key1 和 Key2,另一个持有 Key2 和 Key1。这样,您就可以在没有...嗯...您的答案建议的所有内容的情况下获得任一密钥。【参考方案9】:

我有一个非常简单的方法来做到这一点。它对我来说很完美。

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);

【讨论】:

抱歉,您的回答不符合问题。问题是关于如何通过值找到键,您的答案显示了标准:通过键找到值

以上是关于按值获取字典键的主要内容,如果未能解决你的问题,请参考以下文章

在字典中按值获取键

按值然后键对字典进行排序

使用类提供的排序键按值对字典进行排序

如何在 Python 中直接获取字典键作为变量(而不是通过从值中搜索)?

在 JavaScript 中按值对字典进行排序

如何在 C# 字典中按值索引过滤项目?