Rather than create a whole new class implementing ILookup<T> is it possible to add an extension method to dictionary which caters for it? I'm thinking something along the following:
public static void LookupAdd(this Dictionary<T, List<V>> dict, T key, V item)
{
if (!dict.ContainsKey(key))
{
dict.Add(key, new List<T>());
}
dict[key].Add(item);
}
This fails to compile saying it can't identify the types. I'm guessing that my generic parameters are too complex (particularly List<V>)
You have forgotten to add the generic parameter syntax:
public static void LookupAdd<T, V>(this Dictionary<T, List<V>> dictionary, T key, V item)
{
}
The <T, V> is missing.
Try...
public static void LookupAdd<T,V>(this Dictionary<T, List<V>> dict, T key, V item)
{
if (!dict.ContainsKey(key))
{
dict.Add(key, new List<V>());
}
dict[key].Add(item);
}
UPDATE:
Notice that you should have
new List<V>()
where you have
new List<T>()
Related
I changed a collection from List to Dictionary and it seems I need change from using .ForEach() to foreach() on it - regardless if I'm iterating the Dictionary itself, or Dictionary.Values.
It's no big deal but I've seen a few times slight discrepancies when you can and can't use LINQ, and I wondered if there is a way and I just missed it?
Unfortunately .ForEach() is only defined on the List<> class.
You could either use .ToList() first to get access to .ForEach but that will, of course, lower the performance compared to just using foreach or you could create extension-methods for that:
public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
{
foreach (KeyValuePair<T, U> p in d) { a(p); }
}
public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
{
foreach (T t in k) { a(t); }
}
public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
{
foreach (U u in v) { a(u); }
}
If you are interested in further information, there is already a similar article on SO.
I'm trying to write an extension method for dictionaries to shorten the TryGetValue syntax.
This is the extension and the exact same logic in a normal method.
private static List<T> TryGet<T>(this Dictionary<int, List<T>> dict, int key)
{
return dict.TryGetValue(key, out var output) ? output : new List<T>();
}
private static List<T> TryGet<T>(Dictionary<int, List<T>> dict, int key)
{
return dict.TryGetValue(key, out var output) ? output : new List<T>();
}
var works = TryGet(MyDict, MyInt);
var doesntWork = MyDict.TryGet...
The code simply does not find the extension. I made triple sure that the dictionary is the same and of the right type.
I have the extension code in a file with other working extensions, so that's not the problem either.
The extension methods needs to be accessible for the calling method and should be defined in a static class. In the OP, it is likely that it was defined in a non-static class
public static class Extensions
{
public static List<T> TryGet<T>(this Dictionary<int, List<T>> dict, int key) {
return dict.TryGetValue(key, out var output) ? output : new List<T>();
}
}
Extension methods must be accessible by the caller and in a static class.
public static class Extensions
{
public static List<T> TryGet<T>(this Dictionary<int, List<T>> dict, int key) {
return dict.TryGetValue(key, out var output) ? output : new List<T>();
}
}
Is it possible to write an extension method that works only with dictionaries that have a List as value?
I want to write one that checks if the key is existent, it will add another entry to the list. And if the key is not existent a new list is initialized, the entry added, and then everything added to the dictionary.
Now I am not sure what the method head looks like or if it is even possible to limit the function to a specific value type.
Yes, of course. In the extension method defintition you use List<T>, which the T defined in the type arguments. In this case named TListValue to avoid ambiguity:
public static void DoSomething<TKey, TListValue>(this Dictionary<TKey, List<TListValue>> dictionary)
{
...
}
You can use it without specifying the type parameters. They should be infered:
Dictionary<string, List<string>> u = new Dictionary<string, List<string>>();
u.DoSomething();
Here is a sample implementation of the method that you want to write:
static class DictExtensions {
public static void Insert<TKey,TVal>(this IDictionary<TKey,List<TVal>> d, TKey k, TVal v) {
List<TVal> current;
if (!d.TryGetValue(k, out current)) {
d.Add(k, new List<TVal> { v } );
} else {
current.Add(v);
}
}
}
The name Add would collide with the instance method of Dictionary, so I used the name Insert.
Demo.
Rather than an extension method, I would personally create a class inheriting from Dictionary:
public class ListDictionary<TKey, TValue> : Dictionary<TKey, List<TValue>>
{
new public List<TValue> this[TKey index]
{
get
{
List<TValue> list = null;
if (!TryGetValue(index, out list))
{
list = new List<TValue>();
Add(index, list);
}
return list;
}
set
{
if (ContainsKey(index))
base[index] = value;
else
Add(index, value);
}
}
}
Usage:
ListDictionary<string, string> dictionary = new ListDictionary<string, string>();
dictionary["list1"].Add("item1"); // list will be initialised here
dictionary["list1"].Add("item2");
dictionary["list2"].Add("item1"); // and another
I am trying to create a for loop that invokes a function of several instance of class A in a dictionary, and if there is no value for a key, it creates it and then invokes it.
It seems to me as if there must be a way to create a value upon first access to a key.
I am currently using this code though I think it is not the best possible practice:
(dictionary[i] = dictionary.ContainsKey(arr[i]) ? dictionary[i] : new A()).Push(10);
Is there a cleaner for such a problem in C#?
ConcurrentDictionary has a GetOrAdd method (and other useful methods like AddOrUpdate, TryRemove etc.). If just a plain dictionary had GetOrAdd you could use that...
Luckily, you can create an extension method in a static class which you probably should name DictionaryExtensions:
public static TValue GetOrAdd<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key,
Func<TKey, TValue> valueFactory)
{
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
if (key == null)
throw new ArgumentNullException(nameof(key));
if (valueFactory == null)
throw new ArgumentNullException(nameof(valueFactory));
if (dictionary.TryGetValue(key, out var existingValue))
return existingValue;
var value = valueFactory(key);
dictionary.Add(key, value);
return value;
}
How to use it:
dictionary.GetOrAdd(i, () => new A()).Push(10);
This version uses a value factory so that new A() is only executed in case it is required. Another ConcurrentDictionary.GetOrAdd() overload uses a value provided as parameter which you might consider as an alternative.
I find that creating extension methods like this that closely mirrors the methods on ConcurrentDictionary is very useful.
I'd say a cleaner code would look something like this:
var key = arr[i];
var hasKey = dictionary.ContainsKey(key);
if (!hasKey)
dictionary.Add(key, new A());
var itemToUse = dictionary[key];
itemToUse.Push(10);
Although it seems to me you are looking for something shorter. I guess what you are really asking is a short-hand method that does:
Returns the value for a given key if the key exists, else adds the key to the dictionary with some default value.
I think the above code tells a lot more about the intent, but in case you want something different, I can think of following two solutions.
The first one is an extension method for getting the item:
public static TValue Get<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
var hasKey = dictionary.ContainsKey(key);
if (!hasKey)
dictionary.Add(key, defaultValue);
return dictionary[key];
}
You would use it as:
dict.Get(arr[i], defaultValue: new A())
.Push(10);
The second solution I can think of is a new derivative of Dictionary:
class DefaultDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
private readonly Func<TKey, TValue> _defaultValueFactory;
public DefaultDictionary(TValue defaultValue)
{
_defaultValueFactory = new Func<TKey, TValue>(x => defaultValue);
}
public DefaultDictionary(Func<TValue> defaultValueFactory)
{
_defaultValueFactory = new Func<TKey, TValue>(x => defaultValueFactory()) ?? throw new ArgumentNullException(nameof(defaultValueFactory));
}
public DefaultDictionary(Func<TKey, TValue> defaultValueFactory)
{
_defaultValueFactory = defaultValueFactory ?? throw new ArgumentNullException(nameof(defaultValueFactory));
}
public new TValue this[TKey key]
{
get
{
var hasKey = ContainsKey(key);
if (!hasKey)
{
var defaultValue = _defaultValueFactory(key);
Add(key, defaultValue);
}
return base[key];
}
set
{
base[key] = value;
}
}
}
The usage of this goes like:
var dictionary = new DefaultDictionary<string, A>(() => new A());
// ...
dictionary[arr[i]].Push(10);
I must warn you about something, this derivative of Dictionary hides the index operator. And since using IDictionary as types for members is a common practice (e.g. private IDictionary<string, A> dictionary as a member), you can't use the overloaded version without casting. So either cast your variable to DefaultDictionary every time you want to use the overloaded indexer, or have an interface for this new dictionary like:
interface IDefaultDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
new TValue this[TKey key] { get; set; }
}
And have your members, variables use it as their defining type:
private IDefaultDictionary<string, A> dictionary;
But this also means as a concrete class you must now use DefaultDictionary, and that's the trade-off.
using Microsoft.VisualStudio.TestTools.UnitTesting;
I want universal test method, which gets Dictionary and function, and then check equality for each dictionary entry between Value and function(Key):
public void TestMethod<TKey, TValue>(Dictionary<TKey, TValue> dict, Func<TKey, TValue> func)
{
foreach (var test in dict)
{
Assert.AreEqual(test.Value, func(test.Key));
}
}
But if Values (and return value of function) is
List<int>
it doesnt work, of course. So, I found than I need
CollectionAssert.AreEqual
for such cases.
But now I have to say, that my value is System.Collections.ICollection. How to do this?
You need to cast the values to ICollection so the compiler won't complain.
public void TestMethod<TKey, TValue>(Dictionary<TKey, TValue> dict, Func<TKey, TValue> func)
{
foreach (var test in dict)
{
if (test.Value is ICollection)
{
CollectionAssert.AreEqual((ICollection)test.Value, (ICollection)func(test.Key));
}
else
{
Assert.AreEqual(test.Value, func(test.Key));
}
}
}