C# 获取通用字典指定值的多个键?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/255341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 20:10:04  来源:igfitidea点击:

Getting multiple keys of specified value of a generic Dictionary?

c#.net

提问by Dour High Arch

It's easy to get the value of a key from a .NET generic Dictionary:

从 .NET 通用字典中获取键的值很容易:

Dictionary<int, string> greek = new Dictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
string secondGreek = greek[2];  // Beta

But trying to get the keys given a value is not as straightforward because there could be multiple keys:

但是尝试获取给定值的键并不那么简单,因为可能有多个键:

int[] betaKeys = greek.WhatDoIPutHere("Beta");  // expecting single 2

采纳答案by Jon Skeet

Okay, here's the multiple bidirectional version:

好的,这是多个双向版本:

using System;
using System.Collections.Generic;
using System.Text;

class BiDictionary<TFirst, TSecond>
{
    IDictionary<TFirst, IList<TSecond>> firstToSecond = new Dictionary<TFirst, IList<TSecond>>();
    IDictionary<TSecond, IList<TFirst>> secondToFirst = new Dictionary<TSecond, IList<TFirst>>();

    private static IList<TFirst> EmptyFirstList = new TFirst[0];
    private static IList<TSecond> EmptySecondList = new TSecond[0];

    public void Add(TFirst first, TSecond second)
    {
        IList<TFirst> firsts;
        IList<TSecond> seconds;
        if (!firstToSecond.TryGetValue(first, out seconds))
        {
            seconds = new List<TSecond>();
            firstToSecond[first] = seconds;
        }
        if (!secondToFirst.TryGetValue(second, out firsts))
        {
            firsts = new List<TFirst>();
            secondToFirst[second] = firsts;
        }
        seconds.Add(second);
        firsts.Add(first);
    }

    // Note potential ambiguity using indexers (e.g. mapping from int to int)
    // Hence the methods as well...
    public IList<TSecond> this[TFirst first]
    {
        get { return GetByFirst(first); }
    }

    public IList<TFirst> this[TSecond second]
    {
        get { return GetBySecond(second); }
    }

    public IList<TSecond> GetByFirst(TFirst first)
    {
        IList<TSecond> list;
        if (!firstToSecond.TryGetValue(first, out list))
        {
            return EmptySecondList;
        }
        return new List<TSecond>(list); // Create a copy for sanity
    }

    public IList<TFirst> GetBySecond(TSecond second)
    {
        IList<TFirst> list;
        if (!secondToFirst.TryGetValue(second, out list))
        {
            return EmptyFirstList;
        }
        return new List<TFirst>(list); // Create a copy for sanity
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        greek.Add(5, "Beta");
        ShowEntries(greek, "Alpha");
        ShowEntries(greek, "Beta");
        ShowEntries(greek, "Gamma");
    }

    static void ShowEntries(BiDictionary<int, string> dict, string key)
    {
        IList<int> values = dict[key];
        StringBuilder builder = new StringBuilder();
        foreach (int value in values)
        {
            if (builder.Length != 0)
            {
                builder.Append(", ");
            }
            builder.Append(value);
        }
        Console.WriteLine("{0}: [{1}]", key, builder);
    }
}

回答by dove

revised: okay to have some kind of find you would need something other than dictionary, since if you think about it dictionary are one way keys. that is, the values might not be unique

修订:可以进行某种查找,除了字典之外,您还需要其他东西,因为如果您考虑一下字典是单向键。也就是说,这些值可能不是唯一的

that said it looks like you're using c#3.0 so you might not have to resort to looping and could use something like:

也就是说,看起来您正在使用 c#3.0,因此您可能不必求助于循环,并且可以使用以下内容:

var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true)  == 0 select k.Key).FirstOrDefault();

回答by Domenic

Dictionaries aren't really meant to work like this, because while uniqueness of keys is guaranteed, uniqueness of values isn't. So e.g. if you had

字典并不是真的要这样工作,因为虽然键的唯一性得到保证,但值的唯一性却不是。所以例如,如果你有

var greek = new Dictionary<int, string> { { 1, "Alpha" }, { 2, "Alpha" } };

What would you expect to get for greek.WhatDoIPutHere("Alpha")?

你希望得到greek.WhatDoIPutHere("Alpha")什么?

Therefore you can't expect something like this to be rolled into the framework. You'd need your own method for your own unique uses---do you want to return an array (or IEnumerable<T>)? Do you want to throw an exception if there are multiple keys with the given value? What about if there are none?

因此,您不能指望这样的东西会被纳入框架。您需要自己的方法用于您自己的独特用途---您想返回一个数组(或IEnumerable<T>)吗?如果有多个具有给定值的键,您想抛出异常吗?如果没有呢?

Personally I'd go for an enumerable, like so:

就个人而言,我会选择一个可枚举的,如下所示:

IEnumerable<TKey> KeysFromValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue val)
{
    if (dict == null)
    {
        throw new ArgumentNullException("dict");
    }
    return dict.Keys.Where(k => dict[k] == val);
}

var keys = greek.KeysFromValue("Beta");
int exceptionIfNotExactlyOne = greek.KeysFromValue("Beta").Single();

回答by CMS

Maybe the easiest way to do it, without Linq, can be to loop over the pairs:

也许最简单的方法是在没有 Linq 的情况下遍历这些对:

int betaKey; 
foreach (KeyValuePair<int, string> pair in lookup)
{
    if (pair.Value == value)
    {
        betaKey = pair.Key; // Found
        break;
    }
}
betaKey = -1; // Not found

If you had Linq, it could have done easily this way:

如果你有 Linq,它可以通过这种方式轻松完成:

int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;

回答by Cybis

Can't you create a subclass of Dictionary which has that functionality?

你不能创建一个具有该功能的 Dictionary 的子类吗?


    public class MyDict < TKey, TValue > : Dictionary < TKey, TValue >
    {
        private Dictionary < TValue, TKey > _keys;

        public TValue this[TKey key]
        {
            get
            {
                return base[key];
            }
            set 
            { 
                base[key] = value;
                _keys[value] = key;
            }
        }

        public MyDict()
        {
            _keys = new Dictionary < TValue, TKey >();
        }

        public TKey GetKeyFromValue(TValue value)
        {
            return _keys[value];
        }
    }

EDIT: Sorry, didn't get code right first time.

编辑:对不起,第一次没有得到正确的代码。

回答by tvanfosson

A dictionary doesn't keep an hash of the values, only the keys, so any search over it using a value is going to take at least linear time. Your best bet is to simply iterate over the elements in the dictionary and keep track of the matching keys or switch to a different data structure, perhaps maintain two dictionary mapping key->value and value->List_of_keys. If you do the latter you will trade storage for look up speed. It wouldn't take much to turn @Cybis example into such a data structure.

字典不保留值的散列,只保留键,因此使用值对其进行任何搜索至少需要线性时间。最好的办法是简单地迭代字典中的元素并跟踪匹配的键或切换到不同的数据结构,也许维护两个字典映射 key->value 和 value->List_of_keys。如果您选择后者,您将用存储换取查找速度。将@Cybis 示例转换为这样的数据结构并不需要太多。

回答by Jon Skeet

As everyone else has said, there's no mapping within a dictionary from value to key.

正如其他人所说,字典中没有从值到键的映射。

I've just noticed you wanted to map to from value to multiple keys - I'm leaving this solution here for the single value version, but I'll then add another answer for a multi-entry bidirectional map.

我刚刚注意到您想从值映射到多个键 - 我将这个解决方案留在这里用于单值版本,但我将为多条目双向映射添加另一个答案。

The normal approach to take here is to have two dictionaries - one mapping one way and one the other. Encapsulate them in a separate class, and work out what you want to do when you have duplicate key or value (e.g. throw an exception, overwrite the existing entry, or ignore the new entry). Personally I'd probably go for throwing an exception - it makes the success behaviour easier to define. Something like this:

此处采用的正常方法是拥有两个字典 - 一个映射一种方式,另一种映射方式。将它们封装在一个单独的类中,并计算出当您有重复的键或值时要做什么(例如抛出异常、覆盖现有条目或忽略新条目)。就个人而言,我可能会抛出异常 - 它使成功行为更容易定义。像这样的东西:

using System;
using System.Collections.Generic;

class BiDictionary<TFirst, TSecond>
{
    IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
    IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

    public void Add(TFirst first, TSecond second)
    {
        if (firstToSecond.ContainsKey(first) ||
            secondToFirst.ContainsKey(second))
        {
            throw new ArgumentException("Duplicate first or second");
        }
        firstToSecond.Add(first, second);
        secondToFirst.Add(second, first);
    }

    public bool TryGetByFirst(TFirst first, out TSecond second)
    {
        return firstToSecond.TryGetValue(first, out second);
    }

    public bool TryGetBySecond(TSecond second, out TFirst first)
    {
        return secondToFirst.TryGetValue(second, out first);
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        int x;
        greek.TryGetBySecond("Beta", out x);
        Console.WriteLine(x);
    }
}

回答by dbkk

Dictionary class is not optimized for this case, but if you really wanted to do it (in C# 2.0), you can do:

Dictionary 类没有针对这种情况进行优化,但如果你真的想这样做(在 C# 2.0 中),你可以这样做:

public List<TKey> GetKeysFromValue<TKey, TVal>(Dictionary<TKey, TVal> dict, TVal val)
{
   List<TKey> ks = new List<TKey>();
   foreach(TKey k in dict.Keys)
   {
      if (dict[k] == val) { ks.Add(k); }
   }
   return ks;
}

I prefer the LINQ solution for elegance, but this is the 2.0 way.

为了优雅,我更喜欢 LINQ 解决方案,但这是 2.0 的方式。

回答by Loay

Dictionary<string, string> dic = new Dictionary<string, string>();
dic["A"] = "Ahmed";
dic["B"] = "Boys";

foreach (string mk in dic.Keys)
{
    if(dic[mk] == "Ahmed")
    {
        Console.WriteLine("The key that contains \"Ahmed\" is " + mk);
    }
}

回答by Max Hodges

The "simple" bidirectional dictionary solution proposed here is complex and may be be difficult to understand, maintain or extend. Also the original question asked for "the key for a value", but clearly there could be multiple keys (I've since edited the question). The whole approach is rather suspicious.

这里提出的“简单”双向字典解决方案很复杂,可能难以理解、维护或扩展。最初的问题也要求“值的键”,但显然可能有多个键(我已经编辑了这个问题)。整个方法相当可疑。

Software changes. Writing code that is easy to maintain should be given priority other "clever" complex workarounds. The way to get keys back from values in a dictionary is to loop. A dictionary isn't designed to be bidirectional.

软件变化。编写易于维护的代码应该优先考虑其他“聪明”的复杂解决方法。从字典中的值中取回键的方法是循环。字典不是设计为双向的。