How to make a method generic in c#? - c#

I have couple of methods and it keeps on expanding. So, I am planning to make it generic. Can anyone please help me with that. Atleast the method definition.
private static Dictionary<string, class1> PToDictionary(MapField<string, class1Proto> keyValuePairs)
{
Dictionary<string, class1> keyValues = new();
foreach (var pair in keyValuePairs)
{
**keyValues[pair.Key] = pToR(pair.Value);**
}
return keyValues;
}
My another method is :
private static Dictionary<Uri, class2> PToDictionary1(MapField<string, class2Proto> keyValuePairs)
{
Dictionary<string, class2> keyValues = new();
foreach (var pair in keyValuePairs)
{
**keyValues[new Uri(pair.Key)] = pToR1(pair.Value);**
}
return keyValues;
}
How can I make this generic so that when more methods are added, I dont need to add code.
I was thinking something like this, but errors are :
// Not sure how to call this method after Func is there
private static Dictionary<TKey, TValue> PToDictionary<TKey, TValue, TKeyProto, TValueProto>(MapField<TKeyProto, TValueProto> keyValuePairs, Func<TKeyProto, TKey> keyFunc, Func<TValueProto, TValue> valueFunc)
{
//How can I generalize my above method ?
}
Can someone help me complete the method ?

You don't need an extra method at all. LINQ already provides everything you need, combined with the fact that MapField implements IDictionary<TKey, TValue> (and therefore IEnumerable<KeyValuePair<TKey, TValue>>.
You'd just call:
var dictionary = repeatedField.ToDictionary(
pair => ConvertKey(pair.Key), pair => ConvertValue(pair.Value));
(where ConvertKey would be whatever code you want to convert the repeated field key into the dictionary key, and likewise for ConvertValue).
Sample calls:
var d1 = repeatedField1.ToDictionary(pair => pair.Key, pair => pToR(pair.Value));
var d2 = repeatedField2.ToDictionary(
pair => new Uri(pair.Key), pair => pToR1(pair.Value));
... but you may be able to remove the pToR and pToR1 methods anyway. (It's hard to tell without information about what they're doing...)

You can use the following method to convert MapField<TKeyProto, TValueProto> to Dictionary<TKey, TValue>:
public static Dictionary<TKey, TValue> PToDictionary<TKey, TValue, TKeyProto, TValueProto>(
MapField<TKeyProto, TValueProto> keyValuePairs,
Func<TKeyProto, TKey> mapKey,
Func<TValueProto, TValue> mapValue
)
{
Dictionary<TKey, TValue> keyValues = new();
foreach (var pair in keyValuePairs)
{
keyValues[mapKey(pair.Key)] = mapValue(pair.Value);
}
return keyValues;
}
Here, mapKey is a function that converts MapField's key to a dictionary key. Similarly, mapValue converts MapField's value to a dictionary value.
Another way is to make usage of LINQ ToDictionary extension method:
public static Dictionary<TKey, TValue> PToDictionary<TKey, TValue, TKeyProto, TValueProto>(
MapField<TKeyProto, TValueProto> keyValuePairs,
Func<TKeyProto, TKey> mapKey,
Func<TValueProto, TValue> mapValue
)
{
// this is possible because MapField<TKey, TValue> implements IEnumerable<KeyValuePair<TKey, TValue>>
return keyValuePairs.ToDictionary(
(KeyValuePair<TKeyProto, TValueProto> kvp) => mapKey(kvp.Key),
(KeyValuePair<TKeyProto, TValueProto> kvp) => mapValue(kvp.Value));
}
For example, if you want to convert MapField<string, string> to Dictionary<Uri, int> you can use the following code:
Dictionary<Uri, int> dictionary = PToDictionary(
map,
key => new Uri(key),
val => int.Parse(val));

Related

ReadOnlyDictionary<TKey, TValue> with a ReadOnlyCollection<T>

I have a bunch of classes that need to return properties as ReadOnlyCollection<T>s, which works well when they're just List<T>s, however, things get really sloppy with than list is inside a Dictionary<TKey, TValue>.
================================================
For example, this is a typical List that I return as a ReadOnlyCollection:
private readonly List<string> _encryptionKeys;
public ReadOnlyCollection<string> EncryptionKeys => _encryptionKeys.AsReadOnly();
================================================
However, this is the convulation I need to do for a ReadOnlyDictionary<TKey, TValue>:
private readonly ReadOnlyDictionary<string, ReadOnlyCollection<string>> _attributes;
public ReadOnlyDictionary<string, ReadOnlyCollection<string>> Attributes => _attributes;
... using a temporary variable:
Dictionary<string, List<string>> attributes = new Dictionary<string, List<string>>();
... to which I add values and its lists, I then build my property:
Dictionary<string, ReadOnlyCollection<string>> readonlyListDictionary = new Dictionary<string, ReadOnlyCollection<string>>();
foreach (string key in attributes.Keys)
readonlyListDictionary.Add(key, attributes[key].AsReadOnly());
_attributes = new ReadOnlyDictionary<string, ReadOnlyCollection<string>>(readonlyListDictionary);
================================================
Anyone have any suggestions?
Make it an extension method, then you don't have to think about it the whole time:
public static ReadOnlyDictionary<TKey, ReadOnlyCollection<TValue>> AsReadOnly<TKey, TValue>(this Dictionary<TKey, List<TValue>> source)
{
return new ReadOnlyDictionary<TKey, ReadOnlyCollection<TValue>>(
new Dictionary<TKey, ReadOnlyCollection<TValue>>(
source.Select(kvp =>
new KeyValuePair<TKey, ReadOnlyCollection<TValue>>(kvp.Key, kvp.Value.AsReadOnly())
));
}
Although, the constructor that takes IEnumerable<KeyValuePair> is only available in .NET 5. So in earlier versions we would have to foreach. This option may also be more performant as we pre-size the dictionary
public static ReadOnlyDictionary<TKey, ReadOnlyCollection<TValue>> AsReadOnly<TKey, TValue>(this Dictionary<TKey, List<TValue>> source)
{
var dict = new Dictionary<TKey, ReadOnlyCollection<TValue>>(source.Count);
foreach (var kvp in source)
dict[kvp.Key] = kvp.Value.AsReadOnly();
return new ReadOnlyDictionary<TKey, ReadOnlyCollection<TValue>>(dict);
}
Just use it like you use the other AsReadOnly extension.
Linq can do the heavylifting for you.
Dictionary<string, List<string>> input = new Dictionary<string, List<string>>();
ReadOnlyDictionary<string, ReadOnlyCollection<string>> output = new ReadOnlyDictionary<string, ReadOnlyCollection<string>>(dict.ToDictionary(kv => kv.Key, kv => kv.Value.AsReadOnly()));

Equivalent of java.lang.Number in C#

I want to make arithmetic operations between dictionaries with numbers as value.
This is my code:
public class DictionaryOperation {
public static Dictionary<TKey, double> Add<TKey>(Dictionary<TKey, double> d1, Dictionary<TKey, double> d2) {
Dictionary<TKey, double> result = new Dictionary<TKey, double>();
foreach (TKey key in d1.Keys) {
if (d2.ContainsKey(key))
result[key] = d1[key] + d2[key];
else
result[key] = d1[key];
}
foreach (TKey key in d2.Keys) {
if (!result.ContainsKey(key))
result[key] = d2[key];
}
return result;
}
}
I would like to know if I can create only one method for any numeric type (int float, decimal, ...) or do I have to create one method per numeric type, which means there will be the same code in each methods.
I want to be able to do this:
Dictionary<string, int> ints = DictionaryOperation.Add(new Dictionary<string, int>(), new Dictionary<string, int>());
Dictionary<string, float> floats = DictionaryOperation.Add(new Dictionary<string, float>(), new Dictionary<string, float>());
You can avoid writing the same Method for every numeric type by using generics. You already have a generic key in your dictionary. The only thing missing is the generic value. Change your method to use a generic dictionary value:
public static Dictionary<TKey, TValue> Add<TKey, TValue>(Dictionary<TKey, TValue> d1, Dictionary<TKey, TValue> d2)
where TValue : IComparable
The problem is, that there is no type constraint that allows only numbers (or objects that can be added with the + operator). I used IComparable in the line above because all numeric types are comparable.
The next problem is, the IComparable does not help when trying to use the + operator. For this you can use dynamics like so:
dynamic a = d1[key];
dynamic b = d2[key];
result[key] = a + b;
Now you can use the method for all types that implement IComparable. BUT you have no compile time safety. That means you will get runtime errors for all types that do not implement the + operator.
This problem is already described here:
C# Adding two Generic Values
Here the full method:
public static Dictionary<TKey, TValue> Add<TKey, TValue>(Dictionary<TKey, TValue> d1, Dictionary<TKey, TValue> d2)
where TValue : IComparable
{
Dictionary<TKey, TValue> result = new Dictionary<TKey, TValue>();
foreach (TKey key in d1.Keys) {
if (d2.ContainsKey(key))
{
dynamic a = d1[key];
dynamic b = d2[key];
result[key] = a + b;
}
else
result[key] = d1[key];
}
foreach (TKey key in d2.Keys) {
if (!result.ContainsKey(key))
result[key] = d2[key];
}
return result;
}

How to insert element in first index in dictionary?

Is there a method or technique that allows you to insert an element into a
Dictionary<TKey, TValue> guaranteeing that the item is in the first index of that dictionary's KeyCollection.
For example:
Dictionary<String, String> dic = foo.GetOutput();
// `dic` is something like:
// {"foo", "baa"},
// {"a", "b"}
I need something like:
dic.Add("key", "value", 0);
// where `0` is the index that `key` to be inserted.
foreach(KeyValuePair<String, String> key in dic)
{
Console.WriteLine("{0} = {1}", key.Key, key.Value);
}
Output:
key = value
foo = baa
a = b
By not using a dictionary.
Dictionary<TKey, TValue> is implemented as a hash-table. The position of keys internal to the dictionary depends upon the hash-code, the means by which that hash-code was reduced further to provide an index into its internal structure, and the order of insertion in an entirely implementation-dependant way.
This isn't the only way to implement a dictionary. SortedDictionary<TKey, TValue> uses a tree structure internally and so always keeps keys in an order. In this case we still can't insert something in the beginning, rather we insert something and it gets put in the appropriate place.
If ordering is what you care about most, then you don't want a puredictionary at all. Rather you want either a List<KeyValuePair<TKey, TValue>> or you want a structure that offers both the functionality of a list and of a dictionary, which is provided by OrderedDictionary. This isn't generic, but you can easily create a generic wrapper around it (doesn't give the performance benefits of internally using generics, but does give type-safety in use).
I know it is a three years old question. But found a workaround of this problem. It may help someone
Dictionary<String, String> dic = foo.GetOutput();
dic = (new Dictionary<string, string> {{"key","value"}}).Concat(dic).ToDictionary(k => k.Key, v => v.Value);
This will insert the element in the beginning of dictionary :)
Dictionaries are unordered; elements are meant to be retrieved with a key, whose hash points to its value's location.
What you might want is a List <KeyValuePair>, whose elements can be inserted into a specific index.
List<KeyValuePair<string, string>> list = dic.ToList();
list.Insert(0, new KeyValuePair<string, string>("a", "b"));
foreach(KeyValuePair<string, string> pair in list)
Console.WriteLine("{0} = {1}", pair.Key, pair.Value);
This is not possible with Dictionary<TKey, TValue> as it presents it's values in an unordered fashion when enumerated. There is SortedDictionary<TKey, TValue> which provides ordering but it does so by using an IComparer<TKey> against the key value directly. Here you want the key to be a String and have ordering based on an int. That is not possible with either of these types.
I think you'll need to implement a new type with these very specific semantics in them. For example.
class OrderedMap<TKey, TValue> {
private readonly Dictionary<TKey, TValue> _map = new Dictionary<TKey, TValue>();
private readonly List<TKey> _list = new List<TKey>();
public void Add(TKey key, TValue value) {
if (!_map.ContainsKey(key)) {
_list.Add(key);
}
_map[key] = value;
}
public void Add(TKey key, TValue value, int index) {
if (_map.ContainsKey(key)) {
_list.Remove(key);
}
_map[key] = value;
_list.Insert(index, key);
}
public TValue GetValue(TKey key) {
return _map[key];
}
public IEnumerabe<KeyValuePair<TKey, TValue>> GetItems() {
foreach (var key in _list) {
var value = _map[key];
yield return new KeyValuePair<TKey, TValue>(key, value);
}
}
}
Note this does come with some non-trivial performance differences over a traditional Dictionary<TKey, TValue>. For example Add and Remove are slower.
Dictionary<TKey, TValue> is inherently unordered (or rather, the ordering is unpredictable and shouldn't be relied upon). If you want some sort of ordering, you need to use a different type. It's hard to recommend any particular type without knowing more about your requirements.
The Dictionary<TKey, TValue> can't be ordered.
You can try SortedDictionary<TKey, TValue> instead, but that one is ordered by the Key, not by a separate index.
The Dictionary<TKey,TValue> class does not hold items in an ordered manner, so there is no "first" item.
There is a SortedDictionary<Tkey,TValue> (.NET 4.0+), which sorts by the key, but again, this is a very vague idea of "first".
this is my solution, maybe not the best solution but it works. =)
public static ComboBox FillDropDownList(Dictionary<String, String> dictionary, ComboBox dropDown, String selecione)
{
var d = new SortedDictionary<String, String>();
d.Add("0", selecione);
foreach (KeyValuePair<string, string> pair in dictionary)
{
d.Add(pair.Key, pair.Value);
}
dropDown.DataSource = new BindingSource(d, null);
dropDown.DisplayMember = "Value";
dropDown.ValueMember = "Key";
dropDown.SelectedIndex = 0;
return dropDown;
}
A Dictionary is an un-ordered collection. You could try OrderedDictionary - http://msdn.microsoft.com/en-us/library/system.collections.specialized.ordereddictionary.aspx - which has an Insert() method which is what you're after.

How to convert IEnumerable of KeyValuePair<x, y> to Dictionary?

Is there streamlined way to convert list/enumberable of KeyValuePair<T, U> to Dictionary<T, U>?
Linq transformation, .ToDictionary() extension did not work.
.ToDictionary(kvp=>kvp.Key,kvp=>kvp.Value);
Isn't that much more work.
You can create your own extension method that would perform as you expect.
public static class KeyValuePairEnumerableExtensions
{
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source)
{
return source.ToDictionary(item => item.Key, item => item.Value);
}
}
This is the best I could produce:
public static IDictionary<TKey, TValue> ToDictionary<TKey, TValue>(IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs)
{
var dict = new Dictionary<TKey, TValue>();
var dictAsIDictionary = (IDictionary<TKey, TValue>) dict;
foreach (var property in keyValuePairs)
{
(dictAsIDictionary).Add(property);
}
return dict;
}
I compared the speed of converting an IEnumerable of 20 million key value pairs to a Dictionary using Linq.ToDictionary with the speed of this one. This one ran in 80% of the time of the Linq version. So it's faster, but not a lot. I think you'd really need to value that 20% saving to make it worth using.
Similar to the others, but using new instead of ToDictionary (since new already supports KeyValuePair enumerations) and allowing the passing of an IEqualityComparer<TKey>.
Also including a ToReadOnlyDictionary variant for completeness.
public static class EnumerableKeyValuePairExtensions {
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs, IEqualityComparer<TKey>? comparer = null)
where TKey : notnull
=> new Dictionary<TKey, TValue>(keyValuePairs, comparer);
public static ReadOnlyDictionary<TKey, TValue> ToReadOnlyDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs, IEqualityComparer<TKey>? comparer = null)
where TKey : notnull
=> new ReadOnlyDictionary<TKey, TValue>(keyValuePairs.ToDictionary(comparer));
}

Best way to remove multiple items matching a predicate from a .NET Dictionary?

I need to remove multiple items from a Dictionary.
A simple way to do that is as follows :
List<string> keystoremove= new List<string>();
foreach (KeyValuePair<string,object> k in MyCollection)
if (k.Value.Member==foo)
keystoremove.Add(k.Key);
foreach (string s in keystoremove)
MyCollection.Remove(s);
The reason why I can't directly Remove the items in the foreach block is that this would throw an Exception ("Collection was modified...")
I'd like to do the following :
MyCollection.RemoveAll(x =>x.Member==foo)
But the Dictionary<> class doesn't expose a RemoveAll(Predicate<> Match) method, like the List<> Class does.
What's the best way (both performance wise and elegant wise) to do that?
Here's an alternate way
foreach ( var s in MyCollection.Where(kv => kv.Value.Member == foo).ToList() ) {
MyCollection.Remove(s.Key);
}
Pushing the code into a list directly allows you to avoid the "removing while enumerating" problem. The .ToList() will force the enumeration before the foreach really starts.
you can create an extension method:
public static class DictionaryExtensions
{
public static void RemoveAll<TKey, TValue>(this IDictionary<TKey, TValue> dict,
Func<TValue, bool> predicate)
{
var keys = dict.Keys.Where(k => predicate(dict[k])).ToList();
foreach (var key in keys)
{
dict.Remove(key);
}
}
}
...
dictionary.RemoveAll(x => x.Member == foo);
Instead of removing, just do the inverse. Create a new dictionary from the old one containing only the elements you are interested in.
public Dictionary<T, U> NewDictionaryFiltered<T, U>
(
Dictionary<T, U> source,
Func<T, U, bool> filter
)
{
return source
.Where(x => filter(x.Key, x.Value))
.ToDictionary(x => x.Key, x => x.Value);
}
Modified version of Aku's extension method solution. Main difference is that it allows the predicate to use the dictionary key. A minor difference is that it extends IDictionary rather than Dictionary.
public static class DictionaryExtensions
{
public static void RemoveAll<TKey, TValue>(this IDictionary<TKey, TValue> dic,
Func<TKey, TValue, bool> predicate)
{
var keys = dic.Keys.Where(k => predicate(k, dic[k])).ToList();
foreach (var key in keys)
{
dic.Remove(key);
}
}
}
. . .
dictionary.RemoveAll((k,v) => v.Member == foo);
Starting from the .NET 3.0, it's now allowed to remove items from a Dictionary<TKey,TValue> while enumerating it. According to the documentation:
.NET Core 3.0+ only: The only mutating methods which do not invalidate enumerators are Remove and Clear.
Here is the GitHub issue where this change was proposed and approved: Allow Dictionary<K,V>.Remove during enumeration
So the RemoveAll extension method can be implemented simply like this:
/// <remarks>.NET Core 3.0+ only.</remarks>
public static void RemoveAll<TKey, TValue>(this Dictionary<TKey, TValue> source,
Predicate<KeyValuePair<TKey, TValue>> predicate)
{
foreach (var pair in source)
if (predicate(pair))
source.Remove(pair.Key);
}
Usage example:
myDictionary.RemoveAll(e => e.Value.Member == foo);
The fastest way to remove would be either:
public static void RemoveAll<TKey, TValue>(this IDictionary<TKey, TValue> idict, Func<KeyValuePair<TKey, TValue>, bool> predicate)
{
foreach (var kvp in idict.Where(predicate).ToList())
{
idict.Remove(kvp.Key);
}
}
or
public static void RemoveAll<T>(this ICollection<T> icollection, Predicate<T> predicate)
{
var nonMatchingItems = new List<T>();
// Move all the items that do not match to another collection.
foreach (var item in icollection)
{
if (!predicate(item))
{
nonMatchingItems.Add(item);
}
}
// Clear the collection and then copy back the non-matched items.
icollection.Clear();
foreach (var item in nonMatchingItems)
{
icollection.Add(item);
}
}
depending on whether you have more cases of predicate returning true or not. Both are O(N) in nature, but 1st approach will be faster if you have very less cases of "removal/lookup", and the second one faster if items in collection matches the condition majority of the times.
Can you just change your loop to use an index (i.e. FOR instead of FOREACH)? You'd have to loop backwards, of course, i.e. count-1 down to zero.
Instead of removing just do the inverse (create a new dictionary from the old one containing only the elements you are interested in) and let the garbage collector take care of the old dictionary:
var newDictionary = oldDictionary.Where(x => x.Value != foo);
It's simple using LINQ. Just do the following :)
MyCollection = MyCollection.Where(mc => !keystoremove.Contains(mc.Key))
.ToDictionary(d => d.Key, d => d.Value);

Categories

Resources