基于值,在C#字典中获取键/值对的索引

46

我想知道是否存在某个属性或方法,可以获取特定值的索引。

我发现字典具有Contains()方法,如果传入的值存在,则返回true,因此这种方法几乎实现了我需要的功能。

我知道可以循环遍历所有值对并检查条件,但我提问是因为可能有一种优化的方法来完成这个操作。

8个回答

51

假设你有一个名为fooDictionary的字典

fooDictionary.Values.ToList().IndexOf(someValue);

Values.ToList()将您的字典值转换为一些值对象的列表。

IndexOf(someValue)搜索您的新列表,寻找特定的someValue对象并返回匹配字典键/值对索引的索引。

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

然而,这并不考虑可能存在多个匹配的"someValue"对象的问题。


41

在字典中,并不存在“索引”这样的概念——它本质上是无序的。当然,当您迭代字典中的项时,您会以某种顺序获取这些项,但是该顺序不能保证并且可能随时间改变(特别是如果添加或删除条目)。

显然,您可以通过使用 Key 属性从 KeyValuePair 中获取键,因此这将让您使用字典的索引器:

var pair = ...;
var value = dictionary[pair.Key];
Assert.AreEqual(value, pair.Value);

你并没有明确说明你想要做什么。如果你想要找到与特定值相对应的某个键,你可以使用:

var key = dictionary.Where(pair => pair.Value == desiredValue)
                    .Select(pair => pair.Key)
                    .FirstOrDefault();

如果条目不存在,key 将为 null。

这是基于键类型是引用类型的假设... 如果它是值类型,你需要稍微不同的操作。

当然,如果你真的想通过键查找值,你应该考虑使用另一个字典,除了现有的字典之外,还提供了另一种映射方式。


谢谢,这正是我想要的。我认为我需要提升自己在LINQ方面的技能。我在某个地方读到过,LINQ高度优化,你知道使用该查询是否比foreach循环更好,检查每个值然后返回键? - mjsr
3
不,基本上这将在LINQ内部执行一个foreach循环。实际上还有其他方法可以实现,不是吗? - Jon Skeet

10

考虑使用 System.Collections.Specialized.OrderedDictionary,尽管它不是泛型的,或者实现自己的(示例)。

OrderedDictionary 不支持 IndexOf,但很容易实现:

public static class OrderedDictionaryExtensions
{
    public static int IndexOf(this OrderedDictionary dictionary, object value)
    {
        for(int i = 0; i < dictionary.Count; ++i)
        {
            if(dictionary[i] == value) return i;
        }
        return -1;
    }
}

+1 针对 OrderedDictionary,因为 Dictionary 没有索引。 - Brian
我在问题上犯了一个错误,我指的是键而不是索引,但是你在这里的回答激发了我做其他修改,谢谢。 - mjsr

5
你可以在字典中通过键/值查找索引
Dictionary<string, string> myDictionary = new Dictionary<string, string>();
myDictionary.Add("a", "x");
myDictionary.Add("b", "y");
int i = Array.IndexOf(myDictionary.Keys.ToArray(), "a");
int j = Array.IndexOf(myDictionary.Values.ToArray(), "y");

1
感谢您提供的这段代码片段,它可能会为短期提供一些有限的帮助。通过展示为什么这是一个好的解决方案,适当的解释将极大地提高其长期价值,并使其对未来提出其他类似问题的读者更有用。请[编辑]您的答案以添加一些解释,包括您所做的假设。 - Toby Speight

4
你可以使用LINQ来帮助你完成这个任务。
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "hi");
dict.Add(2, "NotHi");
dict.Add(3, "Bah");

var item = (from d in dict
            where d.Value == "hi"
            select d.Key).FirstOrDefault();

Console.WriteLine(item); //Prints 1

1
在你对max答案的评论中,你说你真正想要的是包含某个值的KeyValuePair中的,而不是索引。你可以编辑你的问题使其更加清晰。
值得指出的是(EricM在他的答案中提到过),一个值可能在字典中出现多次,在这种情况下,人们必须考虑想要获取哪个键:例如第一个出现的,最后一个出现的,还是所有的?
如果您确定每个键都具有唯一值,您可以拥有另一个字典,第一个字典的值作为键,先前的键作为值。否则,这个第二个字典的想法(suggestedJon Skeet 提出)将不起作用,因为您必须再次考虑在新字典中使用哪个可能的键作为值。
如果您问的是索引,EricM的答案就可以了。然后,您可以使用以下方法获取有关的KeyValuePair:
yourDictionary.ElementAt(theIndexYouFound);

只要你不在“yourDictionary”中添加或删除任何内容,就可以。
PS:我知道现在已经过去了近7年,但是无论如何。我认为最好将我的答案表述为针对OP的回答,但现在可以说这是适用于除OP之外的任何其他人的答案。完全意识到这一点,谢谢。

1
如果要搜索一个值,你必须遍历所有的数据。但是为了最小化代码量,你可以使用LINQ。
例如:
给定以下定义的字典:
Dictionary<Int32, String> dict;

你可以使用以下代码:
// Search for all keys with given value
Int32[] keys = dict.Where(kvp => kvp.Value.Equals("SomeValue")).Select(kvp => kvp.Key).ToArray();
        
// Search for first key with given value
Int32 key = dict.First(kvp => kvp.Value.Equals("SomeValue")).Key;

-1

不,对于字典(Dictionary)来说没有类似于IndexOf的方法,但是你可以使用ContainsKey方法来判断一个键是否存在于字典中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接