值为字符串列表的字典

47

我有一个字典,其中我的值是一个列表。 当我添加键时,如果这个键已经存在,我想要将另一个字符串添加到值(列表)中? 如果键不存在,则会创建一个新条目,其中包含一个新列表和值;如果键已存在,则只需向列表值添加一个值。 例如:

Dictionary<string, List<string>> myDic = new Dictionary<string, List<string>>();
myDic.Add(newKey, add to existing list<strings> and not create new one)
7个回答

50
要手动完成这个操作,你需要类似以下的内容:
List<string> existing;
if (!myDic.TryGetValue(key, out existing)) {
    existing = new List<string>();
    myDic[key] = existing;
}
// At this point we know that "existing" refers to the relevant list in the 
// dictionary, one way or another.
existing.Add(extraValue);

然而,在许多情况下,使用 ToLookup 可以使这个过程变得微不足道。例如,考虑一个 List<Person>,你想将它转换成一个字典,按照“姓氏”到“该姓氏的名字列表”的映射方式存储。你可以使用以下代码:

var namesBySurname = people.ToLookup(person => person.Surname,
                                     person => person.FirstName);

如果你在循环中这样做,结果会远非预期。我建议使用如果键不存在,则将其添加为一个空列表作为值。否则,将该值填充为所请求的字符串。 这样更有意义,也更容易编写。 - KappaG3
@KappaG3:我不知道你的意思……特别是关于“填充该值”的部分——我相信我的回答可以满足楼主的需求。 - Jon Skeet
糟糕,我的错。我没有仔细阅读你的代码,认为它做了完全不同的事情。 - KappaG3
新手问题:有人知道namesBySurname的显式类型声明是什么吗?var关键字让我感到困惑。:'( - BenKoshy
2
@BKSpurgeon:您可以始终将鼠标悬停在“var”上以查看确切的类型。在这种情况下,它将是“ILookup<string, string>”。 - Jon Skeet
显示剩余2条评论

21

我会在另一个类中包装这个词典:

public class MyListDictionary
{

    private Dictionary<string, List<string>> internalDictionary = new Dictionary<string,List<string>>();

    public void Add(string key, string value)
    {
        if (this.internalDictionary.ContainsKey(key))
        {
            List<string> list = this.internalDictionary[key];
            if (list.Contains(value) == false)
            {
                list.Add(value);
            }
        }
        else
        {
            List<string> list = new List<string>();
            list.Add(value);
            this.internalDictionary.Add(key, list);
        }
    }

}

3
我认为并没有任何迹象表明“值列表”仅限于包含唯一的值。 - Jon Skeet
我想不出在列表中存储重复字符串的好理由,除了可能是为了提高性能...但这是有争议的。 - sartoris
4
有很多情况下这样做是适当的。比如,在购物系统中对“按订单ID分组”进行分组时,您需要区分“这个订单是一个汉堡”的情况和“这个订单是五个汉堡”的情况。特别地,您已经默默地添加了问题中没有提到的行为。 - Jon Skeet

7

我为此编写了一个字典扩展程序:

public static class DictionaryExtensions
{
    public static void AddOrUpdate(this Dictionary<string, List<string>> targetDictionary, string key, string entry)
    {
        if (!targetDictionary.ContainsKey(key))
            targetDictionary.Add(key, new List<string>());

        targetDictionary[key].Add(entry);
    }
}

现在您可以简单地添加或更新:

using System;
using System.Collections.Generic;
using DictionaryExtensions;

public class Program
{
    public static void Main()
    {
        var newDic = new Dictionary<string, List<string>>();
        newDic.AddOrUpdate("Alpha","Anton");
        newDic.AddOrUpdate("Alpha","Boris");
        newDic.AddOrUpdate("Beta","Doris");
        newDic.AddOrUpdate("Delta","Emil");
        newDic.AddOrUpdate("Alpha","Ceasar");
        
        System.Console.Write(newDic["Alpha"][1].ToString());
    }
}

你的方法接收的参数与你发送的参数不同 - undefined

6
更简单的方法是:
var dictionary = list.GroupBy(it => it.Key).ToDictionary(dict => dict.Key, dict => dict.Select(item => item.value).ToList());

5

在你的字典中创建一个新数组即可。

Dictionary<string, List<string>> myDic = new Dictionary<string, List<string>>();
myDic.Add(newKey, new List<string>(existingList));

0

有一个通用的扩展可以用于任何类型。

public static class DictionaryExtensions
{
    public static void AddOrUpdate<T>(this Dictionary<string, List<T>> targetDictionary, string key, T entry)
    {
        if (!targetDictionary.ContainsKey(key))
            targetDictionary.Add(key, new List<T>());

        targetDictionary[key].Add(entry);
    }
    
    public static void AddRangeOrUpdate<T>(this Dictionary<string, List<T>> targetDictionary, string key, IEnumerable<T> entry)
    {
        if (!targetDictionary.ContainsKey(key))
            targetDictionary.Add(key, new List<T>());

        targetDictionary[key].AddRange(entry);
    }
}

确保使用using System.Collections.Generic; 现在您可以简单地添加或更新一个条目或多个条目。
using Object = UnityEngine.Object;
...
Dictionary<string, List<Object>> res = new Dictionary<string, List<Object>>();
res.AddOrUpdate("somekey", new Object());
res.AddRangeOrUpdate("somekey", new List<Object> { new Object{} });

感谢您为Stack Overflow社区做出的贡献。这可能是一个正确的答案,但如果您能提供代码的额外解释,让开发人员能够理解您的推理过程,那将非常有用。对于不太熟悉语法或难以理解概念的新开发人员来说,这尤其有帮助。您是否可以编辑您的答案,包含更多细节,以造福整个社区? - Jeremy Caney

0

如果您对性能感兴趣,CollectionsMarshal.GetValueRefOrAddDefault API 是您的好朋友。该 API 从 .NET 6 开始可用,并允许使用单个哈希值执行 Add 操作:

public static void Add<TKey, TValue>(this Dictionary<TKey, List<TValue>> source,
    TKey key, TValue value) where TKey : notnull
{
    ArgumentNullException.ThrowIfNull(source);
    ref List<TValue> listRef = ref CollectionsMarshal
        .GetValueRefOrAddDefault(source, key, out _);
    listRef ??= new List<TValue>();
    listRef.Add(value);
}

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