【问题标题】: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 框架有更好的答案。

标签: c# 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】:

你可以这样做:

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

如果不考虑性能,请使用方法 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);
        }
        

        【讨论】:

        • 抱歉,您的回答不符合问题。问题是关于如何通过值找到键,您的答案显示了标准:通过键找到值
        猜你喜欢
        • 2014-06-11
        • 2011-12-22
        • 2022-12-05
        • 2017-01-29
        • 2013-01-02
        相关资源
        最近更新 更多