简体   繁体   English

在字典上添加“.Add”,列表为值

[英]“.Add” on Dictionary with a list as value

I've been struggling to Google this question as I can't get the wording quite right (hence the title). 我一直在努力向谷歌这个问题努力,因为我无法得到正确的措辞(因此标题)。

The gist is why do one of the below work, is there a shorthand for test3 : 要点是为什么以下工作之一,是否有test3的简写:

  var test1 = new Dictionary<string, int>();
  test1["Derp"] = 10; // Success

  var test2 = new Dictionary<string, List<int>>();
  test2["Derp"].Add(10); // Fail

  var test3 = new Dictionary<string, List<int>>();
  test3["Derp"] = new List<int>();
  test3["Derp"].Add(10); // Success

A scenario I'm coming across often is similar to the below (this is a very basic example): 我经常遇到的情况类似于下面的情况(这是一个非常基本的例子):

  var names = new List<string>() { "Jim", "Fred", "Fred", "Dave", "Jim", "Jim", "Jim" };

  var nameCounts = new Dictionary<string, int>();

  foreach(var name in names)
  {
    if (!nameCounts.ContainsKey(name))
      nameCounts.Add(name, 0);

    nameCounts[name]++;
  }

In other words - is there a way to skip the "ContainsKey" check, and go straight to adding to my list (and key automatically)? 换句话说 - 有没有办法跳过“ContainsKey”检查,直接添加到我的列表(并自动键入)?

Edit: to be clear, I hadn't used the below as in my real-life situation, it isn't quite as simple (unfortunately!) 编辑:要清楚,我没有使用下面的现实生活情况,它不是那么简单(不幸的是!)

var nameCounts = names.GroupBy(x => x)
                      .ToDictionary(x => x.Key, x => x.Count());

Perl calls this auto-vivification, and I use some extensions to Dictionary to implement various forms, you would need the one that uses a lambda to generate the initial values: Perl称这种自动生成,我使用一些扩展到Dictionary来实现各种形式,你需要使用lambda来生成初始值:

//***
// Enhanced Dictionary that auto-creates missing values with seed lambda
// ala auto-vivification in Perl
//***
public class SeedDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
    Func<TValue> seedFn;
    public SeedDictionary(Func<TValue> pSeedFn) : base() {
        seedFn = pSeedFn;
    }
    public SeedDictionary(Func<TValue> pSeedFn, IDictionary<TKey, TValue> d) : base() {
        seedFn = pSeedFn;
        foreach (var kvp in d)
            Add(kvp.Key, kvp.Value);
    }

    public new TValue this[TKey key]
    {
        get
        {
            if (!TryGetValue(key, out var val))
                base[key] = (val = seedFn());
            return val;
        }
        set => base[key] = value;
    }
}

So then you could do test2 like so: 那么你可以像这样做test2:

var test2 = new SeedDictionary<string, List<int>>(() => new List<int>());
test2["Derp"].Add(10); // works

For your name counts example, you could use the version that auto-creates the default value for the value type: 对于您的名称计数示例,您可以使用自动为值类型创建默认值的版本:

//***
// Enhanced Dictionary that auto-creates missing values as default
// ala auto-vivification in Perl
//***
public class AutoDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
    public AutoDictionary() : base() { }
    public AutoDictionary(IDictionary<TKey, TValue> d) : base() {
        foreach (var kvp in d)
            Add(kvp.Key, kvp.Value);
    }

    public new TValue this[TKey key]
    {
        get
        {
            if (!TryGetValue(key, out var val))
                base[key] = val;
            return val;
        }
        set => base[key] = value;
    }
}

Another way you can do this (among many), is a little extension method (cutesy of Jon Skeet here ) 你可以做到这一点的另一种方式(在众多中)是一种小扩展方法(Jon Skeet 在这里很可爱)

public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary,TKey key) where TValue : new() 
{
    TValue ret;
    if (!dictionary.TryGetValue(key, out ret))
    {
        ret = new TValue();
        dictionary[key] = ret;
    }
    return ret; 
 }

Usage 用法

strong textvar test2 = new Dictionary<string, List<int>>();
var myNewList = test2.GetOrCreate("Derp");
myNewList.Add(10); 

// or

var test2 = new Dictionary<string, List<int>>();
test2.GetOrCreate("Derp").Add(10); // winning!

Note : In all my early morning pep, i actually didn't look at this question, Eric Lippert is on the money in the comments, this can be simply done via a GroupBy and a projection to a dictionary with ToDictionary without all the extra fluff of extension methods and classes 注意 :在我所有的清晨,我实际上并没有看到这个问题, Eric Lippert在评论中有钱,这可以通过GroupBy简单地完成,并使用ToDictionary投影到字典而不需要额外的ToDictionary扩展方法和类

Cutesy of Eric Lippert Eric Lippert的 Cutesy

// Count occurrences of names in a list 
var nameCounts = names.GroupBy(x => x)
                      .ToDictionary(x => x.Key, x => x.Count());

Additional Resources 其他资源

Enumerable.GroupBy Method Enumerable.GroupBy方法

Groups the elements of a sequence. 对序列的元素进行分组。

Enumerable.ToDictionary Method Enumerable.ToDictionary方法

Creates a Dictionary<TKey,TValue> from an IEnumerable<T> . IEnumerable<T>创建一个Dictionary<TKey,TValue> IEnumerable<T>

I usually do something like this: 我通常做这样的事情:

TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
        where TValue : new()
        => dict.TryGetValue(key, out TValue val) ? val : dict[key] = new TValue();

Edit: Another way is: 编辑:另一种方式是:

TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
        where TValue : new()
        => dict.ContainsKey(key) ? dict[key] : dict[key] = new TValue();

I'm not sure if this is as performant, but it works on older C# versions, where my first example doesn't. 我不确定这是否具有高性能,但它适用于较旧的C#版本,我的第一个例子没有。

Alternative with C# 7 out variable : 替代C#7输出变量

foreach(var name in names)
{
    nameCounts[name] = nameCounts.TryGetValue(name, out var count) ? count + 1 : 1;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM