Sort Dictionary<> on value, lookup index from key - c#

I have a Dictionary<> which I want to sort based on value so I've done this by putting the dictionary into a List<> then using the .Sort method.
I've then added this back into a Dictionary<>. Is it possible to lookup the new index/order by using the Dictionary key??
Dictionary<int, MyObject> toCompare = new Dictionary<int, MyObject>();
toCompare.Add(0, new MyObject());
toCompare.Add(1, new MyObject());
toCompare.Add(2, new MyObject());
Dictionary<int, MyObject> items = new Dictionary<int, MyObject>();
List<KeyValuePair<int, MyObject>> values = new List<KeyValuePair<int, MyObject>> (toCompare);
// Sort.
values.Sort(new MyComparer());
// Convert back into a dictionary.
foreach(KeyValuePair<int, PropertyAppraisal> item in values)
{
// Add to collection.
items.Add(item.Key, item.Value);
}
// THIS IS THE PART I CAN'T DO...
int sortedIndex = items.GetItemIndexByKey(0);

Keep your data in the Dictionary<TKey,TValue>, but use a List<TKey> to sort the keys, then iterate as such:
IDictionary<int, MyObject> dict = new Dictionary<int, MyObject>();
// ... Populate dict with data.
IList<int> keyList = new List<int>();
keyList.AddRange(dict.Keys);
// Sort keyList based on key's value.
// MyObject must implement IComparable<MyObject>.
keyList.Sort(delegate(int x, int y) {
return dict[x].CompareTo(dict[y]);
});
foreach (int key in keyList) {
MyObject value = dict[key];
}
This way, your list is merely a sorted index and does not affect your storage algorithm.

Take this extension method:
public static Dictionary<TKey, TValue> Sort<TKey, TValue, TSortingKey>(this Dictionary<TKey, TValue> source,
Func<KeyValuePair<TKey, TValue>, TSortingKey> selector)
{
var result = new Dictionary<TKey, TValue>();
foreach (var pair in source.OrderBy(selector))
result.Add(pair.Key, pair.Value);
return result;
}
And usage:
Dictionary<int, MyType> source = new Dictionary<int, MyType>();
Dictionary<int, MyType> sortedDictionary = source.Sort(i => i.Value.Property1); //sort dictionary by values (by property "Property1" of type MyType
Hope this helps

Related

How can I merge two dictionaries without getting ArgumentException on the key?

I can't figure out how to keep the keys and values on a dictionary when I try to merge two dictionaries. I keep getting ArgumentException due to duplicate of key. When the key match I would just like to add the value by =+ kvp.value;
I have a list of Dictionaries where the
1st Dictionary = kvp = "jump", 2;
2ndDictionary = kvp = "jump", 4;
I like to merge them and get something like:
Dictionary = kvp = "jump", 6;
That I can later add to my list of Dictionaries
I've tried to run something I found in StackOverflow thread.
foreach (var dict in listOfDict)
{
dict.SelectMany(d => d)
.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
}
But I keep getting.
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
I want to avoid getting all keys and all values on separate lists that I later loop through to add key and value on a new dictionary.
Simplest extension to list of dictionary of double values with using Linq:
public static class ExtListOfDict {
public static Dictionary<TKey, double> SumValue1<TKey>(this List<Dictionary<TKey, double>> list)
=> list?.SelectMany(i => i).ToLookup(i => i.Key, i => i.Value).ToDictionary(i => i.Key, i => i.Sum());
}
without linq:
public static Dictionary<TKey, double> SumValue2<TKey>(this List<Dictionary<TKey, double>> list) {
if(list?.Count > 0) {
var dir = new Dictionary<TKey, double>(list[0]);
for(var i = 1; i < list.Count; i++)
foreach (var kv in list[i])
if (dir.TryGetValue(kv.Key, out double sum))
dir[kv.Key] = sum + kv.Value;
else
dir.Add(kv.Key, kv.Value);
return dir;
} else
return null;
}
If you like the LINQ approach, I would go with something like this:
var dictionaries = new List<Dictionary<string, int>>(); // this is the list of dictionaries you want to merge
var unifiedDictionary = new Dictionary<string, int>(); // this is the dictionary where you merge and add the values
foreach (var kvp in dictionaries.SelectMany(dictionary => dictionary))
{
if (unifiedDictionary.ContainsKey(kvp.Key))
{
unifiedDictionary[kvp.Key] += kvp.Value;
}
else
{
unifiedDictionary.Add(kvp.Key, kvp.Value);
}
}
However, if this is too hard to read (I am not always a fan of excessive LINQ over explicit code blocks), you can use the for-loop approach:
var dictionaries = new List<Dictionary<string, int>>(); // this is the list of dictionaries you want to merge
var unifiedDictionary = new Dictionary<string, int>(); // this is the dictionary where you merge and add the values
foreach (var dictionary in dictionaries)
{
foreach (var kvp in dictionary)
{
if (unifiedDictionary.ContainsKey(kvp.Key))
{
unifiedDictionary[kvp.Key] += kvp.Value;
}
else
{
unifiedDictionary.Add(kvp.Key, kvp.Value);
}
}
}
Hope this helps you. If further help and explanations are needed, please tell me.
Here is a solution based on the CollectionsMarshal.GetValueRefOrAddDefault API (.NET 6), and on the INumber<TSelf> interface (.NET 7):
public static Dictionary<TKey, TValue> ToSumDictionary<TKey, TValue>(
this IEnumerable<Dictionary<TKey, TValue>> dictionaries)
where TValue : struct, INumber<TValue>
{
ArgumentNullException.ThrowIfNull(dictionaries);
Dictionary<TKey, TValue> result = null;
foreach (var dictionary in dictionaries)
{
if (result is null)
{
result = new(dictionary, dictionary.Comparer);
continue;
}
if (!ReferenceEquals(dictionary.Comparer, result.Comparer))
throw new InvalidOperationException("Incompatible comparers.");
foreach (var (key, value) in dictionary)
{
ref TValue refValue = ref CollectionsMarshal
.GetValueRefOrAddDefault(result, key, out bool exists);
refValue = exists ? refValue + value : value;
}
}
result ??= new();
return result;
}
The key of each KeyValuePair<TKey, TValue> in each dictionary is hashed only once.
If you are getting an exception due to duplicate keys, then it sounds like you have duplicate keys!
Have you checked the two dictionaries before you try to merge them? Simply calling =+ kvp.value without checking to see if the first dictionary already has a key of that name is very likely to be your problem.
You need to check for an existing entry with that key, and if one is found, take whatever action is appropriate for your scenario (ie ignore, overwrite, ask the user to decide, etc)

Is it possible to store a KeyValuePair of a dictionary without the use of a foreach loop?

The syntax for iterating over a dictionary with a foreach loop is:
foreach (KeyValuePair<key, value> item in dictionary)
Inside the foreach loop the key is accessed with item.key and the value with item.value.
This got me thinking, can this be used without the use of a foreach loop as a convenient (although niche) way to represent a specific dictionary pair?
I am not looking for some weird work arounds, like running a foreach loop and saving the KeyValuePair into a variable once the target key is reached, because at this point it would be more convenient to just use 2 variables.
Like this
var dic = new Dictionary<string, int>();
dic["a"] = 42;
KeyValuePair<string, int> keyVal;
foreach(var kv in dic) {
keyVal = kv; << gets the last entry from the dictioanry
}
Note that the dictionary does not store KeyValuePairs, it creates one for the enumeration, so the simple thing to do is this (because we are not expensively recreating something)
var dic = new Dictionary<string, int>();
dic["a"] = 42;
KeyValuePair<string, int> keyVal = new KeyValuePair<string, int>("a", dic["a"]);
this is more efficient than the (neat) LINQ Sinlge method
The IDictionary<TKey, TValue> interface implements IEnumerable<KeyValuePair<TKey,TValue>>. This means you can simply use Single() to get the entry you want.
IDictionary<string, int> dict = ...;
KeyValuePair<string, int> entry = dict.Single(it => it.Key == "yourKey");
try this
var dict = new Dictionary<string, string>() {
{"hi","Hello World!"},
{"adieu","Goodby"}
};
string hi = dict["hi"]; //Hello World!
or if you want a list
List<KeyValuePair<string,string>> list = dict.ToList();
result
[{"Key":"hi","Value":"Hello World!"},{"Key":"adieu","Value":"Goodby"}]

How List<T> works with Dictionary<K, List<T>>?

var solutions = new Dictionary<int, List<Tuple<int, int>>>();
List<Tuple<int, int>> list = null;
var z = solutions.TryGetValue(sum, out list);
if (!z)
{
list = new List<Tuple<int, int>>();
solutions.Add(sum, list);
}
list.Add(new Tuple<int, int>(a, b));
I don't understand why when I add element to list (last line) it change corresponding key-value in dictionary?
You don't change key value pair in dictionary by adding element to list. You are changing the list. And dictionary is just holding reference to that list so it appears to change.

How can I get the inner list value of list of keyvaluepair of (string, list)?

I am now struggling to find a way to get inner list from complicated List of keyvalupair.
public static List<KeyValuePair<string, List<KeyValuePair<int, string>>>> conditions = new List<KeyValuePair<string, List<KeyValuePair<int, string>>>>();
I want list from above list using given string value as a key.
Can anyone help me please?
You can use FirstOrDefault method like this:
conditions.FirstOrDefault(x => x.Key == "key").Value;
Or you should probably use a Dictionary instead.
You could use a Dictionary and a Dictionary as your value as well.
public static Dictionary<string, Dictionary<int, string>> conditions = new Dictionary<string, Dictionary<int, string>>();
and access it with conditions[key]
For example:
//Initialize random, pointless dictionary for example
var conditions = new Dictionary<string, Dictionary<int, string>>
{
{
"firstDict", new Dictionary<int, string>
{
{1, "blue"},
{2, "red"}
}
},
{
"secondDict", new Dictionary<int, string>
{
{1, "car"},
{2, "truck"}
}
}
};
//Get dictionary with key value "firstDict"
var firstDict = conditions["firstDict"];
//Gets the value associated with key "1"
var color = firstDict[1];

Dictionary enumeration in C#

How do I enumerate a dictionary?
Suppose I use foreach() for dictionay enumeration. I can't update a key/value pair inside foreach(). So I want some other method.
To enumerate a dictionary you either enumerate the values within it:
Dictionary<int, string> dic;
foreach(string s in dic.Values)
{
Console.WriteLine(s);
}
or the KeyValuePairs
foreach(KeyValuePair<int, string> kvp in dic)
{
Console.WriteLine("Key : " + kvp.Key.ToString() + ", Value : " + kvp.Value);
}
or the keys
foreach(int key in dic.Keys)
{
Console.WriteLine(key.ToString());
}
If you wish to update the items within the dictionary you need to do so slightly differently, because you can't update the instance while enumerating. What you'll need to do is enumerate a different collection that isn't being updated, like so:
Dictionary<int, string> newValues = new Dictionary<int, string>() { 1, "Test" };
foreach(KeyValuePair<int, string> kvp in newValues)
{
dic[kvp.Key] = kvp.Value; // will automatically add the item if it's not there
}
To remove items, do so in a similar way, enumerating the collection of items we want to remove rather than the dictionary itself.
List<int> keys = new List<int>() { 1, 3 };
foreach(int key in keys)
{
dic.Remove(key);
}
In answer to the problem "I can't update value/key inside foreach()", you cannot modify a collection while enumerating it. I would approach this by making a copy of the Keys collection:
Dictionary<int,int> dic=new Dictionary<int, int>();
//...fill the dictionary
int[] keys = dic.Keys.ToArray();
foreach (int i in keys)
{
dic.Remove(i);
}
Foreach. There are three ways: You can enumerate over the Keys property, over the Values property or over the dictionary itself which is an enumerator of KeyValuePair<TKey, TValue>.
I just answered the same (updated) question for lists, so here's the same thing for dictionaries.
public static void MutateEach(this IDictionary<TKey, TValue> dict, Func<TKey, TValue, KeyValuePair<TKey, TValue>> mutator)
{
var removals = new List<TKey>();
var additions = new List<KeyValuePair<TKey, TValue>>();
foreach (var pair in dict)
{
var newPair = mutator(pair.Key, pair.Value);
if ((newPair.Key != pair.Key) || (newPair.Value != pair.Value))
{
removals.Add(pair.Key);
additions.Add(newPair);
}
}
foreach (var removal in removals)
dict.Remove(removal);
foreach (var addition in additions)
dict.Add(addition.Key, addition.Value);
}
Note that we have to do the updates outside the loop, so we aren't modifying the dictionary as we enumerate it. Also this detects clashes caused by making two keys the same - it will throw (due to the use of Add).
Example - make all keys lowercase and trim all values, with a Dictionary<string, string>:
myDict.MutateEach(key => key.ToLower(), value => value.Trim());
If the keys are not unique when made lowercase, this will throw.

Categories

Resources