«.Add» в словаре со списком в качестве значения - PullRequest
3 голосов
/ 28 июня 2019

Я пытался ответить на этот вопрос в Google, так как не могу получить совершенно правильную формулировку (отсюда и название).

Суть в том, почему одна из приведенных ниже работ, есть ли сокращение для 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

Сценарий, с которым я часто сталкиваюсь, похож на приведенный ниже (это очень простой пример):

  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]++;
  }

В другихслова - есть ли способ пропустить проверку «ContainsKey» и сразу перейти к добавлению в мой список (и автоматически ввести ключ)?

Редактировать: чтобы быть понятным, я не использовал нижеприведенное, как в моемв реальной жизни все не так просто (к сожалению!)

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

Ответы [ 4 ]

3 голосов
/ 28 июня 2019

Perl вызывает это авто-вивификацию, и я использую некоторые расширения для Dictionary для реализации различных форм, вам понадобится та, которая использует лямбду для генерации начальных значений:

//***
// 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;
    }
}

Итаквы можете сделать test2 следующим образом:

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

В качестве примера для подсчета имен вы можете использовать версию, которая автоматически создает значение по умолчанию для типа значения:

//***
// 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;
    }
}
2 голосов
/ 28 июня 2019

Еще один способ сделать это (среди многих) - это небольшой метод расширения (любезно предоставленный Джоном Скитом здесь )

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; 
 }

Использование

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!

Примечание : Во время всего моего раннего утреннего бодрствования я фактически не смотрел на этот вопрос, Эрик Липперт находится на деньгах в комментариях, это можно сделать просто через GroupBy и проекцию на словарь с ToDictionary без всего лишнего потока методов и классов расширения

Cutesy of Эрик Липперт

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

Дополнительные ресурсы

Метод Enumerable.GroupBy

Группирует элементы последовательности.

Метод Enumerable.ToDictionary

Создает Dictionary<TKey,TValue> из IEnumerable<T>.

0 голосов
/ 28 июня 2019

Альтернатива с переменной C # 7 :

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

0 голосов
/ 28 июня 2019

Я обычно делаю что-то вроде этого:

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();

Редактировать: Другой способ:

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

Я не уверен, что это так же эффективно, но работает на старыхВерсии C #, где мой первый пример не.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...