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

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

Related

How to make a method generic in 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));

C# compare lists List<T>

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

Dictionary to Custom KeyValuePair list - can't convert (C# .Net 4.0)

I read that dictionary and KeyValuePair can not be written by the xml serializer.
So I wrote my own KeyValuePair struct.
public struct CustomKeyValuePair<Tkey, tValue>
{
public Tkey Key { get; set; }
public tValue Value { get; set; }
public CustomKeyValuePair(Tkey key,tValue value) : this()
{
this.Key = key;
this.Value = value;
}
}
But when I do this, I get an error, that it can't convert:
List<CustomKeyValuePair<string, AnimationPath>> convList =
Templates.ToList<CustomKeyValuePair<string, AnimationPath>>();
It works on the normal keyValuePair, but not on my custom one. So whats the problem?
I tried to copy the original as close as possible, but it doesn't want to convert my dictionary (Templates) to that list. I can't see that it uses any interface or inherits from a struct to do that. Do I have to add all the entries manually?
Dictionary<Tkey, TValue> implements both IEnumerable<KeyValuePair<Tkey, Tvalue>> and ICollection<KeyValuePair<Tkey, Tvalue>>:
(from metadata shown in Visual Studio):
public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>,
ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>,
IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback
That's why ToList() with KeyValuePair works and the other doesn't.
Your best bet is probably to use:
List<CustomKeyValuePair<string, AnimationPath>> convList =
Templates.Select(kv => new CustomKeyValuePair(kv.Key, kv.Value)).ToList();

Create Extension Method TryGetValue(TKey, out TValue) of Enumerable<KeyValuePair<TKey, TValue>> just like of Dictionary<TKey, TValue>

I have a Enumerable<KeyValuePair<TKey, TValue>>. I want to create a bool TryGetValue(TKey, out TValue) extension method of it just like it is available in Dictionary<TKey, TValue>.
I tried
public static bool TryGetValue<TKey, TValue>
(this Enumerable<KeyValuePair<TKey, TValue>> mapping, TKey key, out TValue value)
{
bool retVal = false;
KeyValuePair<TKey, TValue> kvp;
kvp = mapping.First(x => x.Key.Equals(key));
if(kvp.Key == null && kvp.Value == null)
{
retVal = false;
value = default(TValue);
}
else
{
retVal = true;
value = kvp.Value;
}
return retval;
}
Is this correct way? If not please suggest one.
Note:
I cannot use a Dictionary because Keys are repeated. Moreover it will only return the first matching value?
What happens to the rest?
We can leave them. I am sending KeyValuePair created from a DataTable. I am creating that DataTable using order by columnname in its query.
Why not just use a simple foreach loop?
Example:
public static bool TryGetValue<TKey, TValue>
(this KeyValuePair<TKey, TValue>[] mapping, TKey key, out TValue value)
{
foreach(var kvp in mapping)
if (kvp.Key.Equals(key))
{
value = kvp.Value;
return true;
}
value = default(TValue);
return false;
}
Your implementation will throw an exception if the key doesn't exists due to .First(), and FirstOrDefault() would be ugly since KeyValuePair is a struct and hence you can't just compare it to null.
Sidenote:
Instead of extending KeyValuePair<TKey, TValue>[], you probably want to use IEnumerable<KeyValuePair<TKey, TValue>> instead to be more flexible.

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