I have a Dictionary<string, int> where the string is a randomized collection of characters and the int is the ASCII sum of that string.
e.g.
["aaaaaaaaab", 971],
["aaaaaaaaba", 971],
["aaaaaaabaa", 971],
["aaaaaabaaa", 971]
I would like to make a new dictionary from the original where the new key is the value from the original, and the new value is the List<string> which would contain all the strings with the key as the ASCII sum.
e.g.
[971, List<string>{ "aaaaaaaaab", "aaaaaaaaba", "aaaaaaabaa", "aaaaaabaaa"}]
How can I achieve this? I cannot wrap my head around the required steps.
Use could GroupBy and ToDictionary
The premise is :
group by the old Value
project to a new dictionary given the values of the GroupBy
which will be the grouped list of KeyValuePair from the original dictionary, that in-turn has the key selected out of it (.Select(y => y.Key)
Example
var newDict = old.GroupBy(x => x.Value)
.ToDictionary(x => x.Key, x => x.Select(y => y.Key)
.ToList());
Additional Resources
Enumerable.GroupBy Method
Groups the elements of a sequence.
Enumerable.ToDictionary Method
Creates a Dictionary<TKey,TValue> from an IEnumerable<T>.
Since values are not unique, you need to group by Value before converting to dictionary:
var inverse = original
.GroupBy(p => p.Value)
.ToDictionary(g => g.Key, g => g.Select(p => p.Key).ToList());
If you wanted to do this without Linq, you could do the following:
foreach(KeyValuePair<string, int> entry in dict) {
if(!dict2.ContainsKey(entry.Value)) {
dict2[entry.Value] = new List<string>();
}
dict2[entry.Value].Add(entry.Key);
}
Assuming you have dict defined as Dictionary<string, int> dict and dict2 defined as Dictionary<int, List<string>> dict2
Here is a complete example for anyone that wants to "wrap their head around" how to do this, without LINQ.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Dictionary<string,int> origDict = new Dictionary<string,int>{{"tttt",1},{"fttt",1},{"fftt",2}};
var vals = new int[origDict.Count];
origDict.Values.CopyTo(vals,0);
var keys = new string[origDict.Count];
origDict.Keys.CopyTo(keys,0);
Dictionary<int,List<string>> newDict = new Dictionary<int,List<string>>();
for(int i = 0; i < vals.Length; i++){
int val = vals[i];
if(newDict.ContainsKey(val)){
newDict[val].Add(keys[i]);
}else{
newDict[val] = new List<string>();
newDict[val].Add(keys[i]);
}
}
foreach(var key in newDict.Keys){
Console.WriteLine(key);
foreach(var val in newDict[key]){
Console.WriteLine(val);
}
}
}
}
Output:
1
tttt
fttt
2
fftt
Related
I have a Dictionary with Keys and Values. Is it possible to get all Key who has the same Value?
Example 1=456894, 2=548962, 3=548962, 4=654876 and then get the Key 2 and 3 because it has the same value.
Dictionary<int, int> doublechek = new Dictionary<int, int>();
One possible approach:
var result = doublechek
.GroupBy(z => z.Value)
.Where(z => z.Count() > 1)
.SelectMany(z => z)
.Select(z => z.Key)
.ToList();
GroupBy and Count will get only those with duplicates. SelectMany and Key will get the keys of those with duplicates (i.e. 2 and 3).
I would create a new "flipped" dictionary, containing all the unique values from doublecheck as keys, and for each key a list of values containing all the keys from doublecheck that have the referenced value.
Like so:
Dictionary<int, List<int>> flipped = new Dictionary<int, List<int>>();
foreach (var key in doublecheck.Keys)
{
if (!flipped.ContainsKey(doublecheck[key]))
flipped.Add(doublecheck[key], new List<int>());
flipped[doublecheck[key]].Add(key);
}
The dictionary flipped can be used to find all the keys of doublecheck that have the value you are searching.
Update
When using TryGetValue the code looks like this:
Dictionary<int, List<int>> flipped = new Dictionary<int, List<int>>();
foreach (var key in doublecheck.Keys)
{
if (!flipped.TryGetValue(doublecheck[key], out var valueList);
{
valueList = new List<int>();
flipped.Add(doublecheck[key], valueList);
}
valueList.Add(key);
}
return true;
I have a Dictionary like this:
Dictionary<string, object> properties = new Dictionary<string, object>()
{
{"aa:bb", MyObject1},
{"aa:cc", MyObject2},
{"dd:xx", MyObject3},
{"dd:yy", MyObject4}
};
The key of the dictionary is a string with ':' as delimiter. Now I want do create from that Dictionary a new one:
Dictionary<string, object> ddProperties = new Dictionary<string, object>()
{
{"xx", MyObject3},
{"yy", MyObject4}
};
I'm looking for an elegant way to create the new Dictionary by splitting the key of the original Dictionary. Is that possible with LINQ?
Try:
var ddProperties = properties.ToDictionary
(kvp => kvp.Key.Split(':')[1], kvp => kvp.Value);
If you only need the ones beginning with dd (as can be seen in your sample output), I would do:
var filteredPairs = from kvp in properties
let split = kvp.Key.Split(':')
where split[0] == "dd"
select new { Key = split[1], kvp.Value };
var ddProperties = filteredPairs.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Do note that there is no protection against duplicates here. If you're expecting duplicate keys after transformation, how would you like to handle them?
note that since each entry is multiple results, use .SelectMany().
As mentioned, ToDictionary builds dictionaries nicely
properties
.SelectMany(kvp => kvp.Key.Split(':')
// Select each item in the split
// to gain access to the original Key Value Pair parameter
.Select(key => new { Key = key, Value = kvp.Value }))
.ToDictionary(
a => a.Key,
a => a.Value);
How Can I Add additional Dictionary Object below
var topLevel1 = resultRows.GroupBy(g => g["CustomerAccountType"])
.ToDictionary(g => g.Key, g => g.ToList());
I want to add this within the LINQ line
new Dictionary<string, object> { {"children", someDictionaryAgain}}
I want to append additional dictionary object after the .ToDictionary()
something like
var topLevel1 = resultRows.GroupBy(g => g["CustomerAccountType"])
.ToDictionary(g => g.Key, g => g.ToList()).Add(new Dictionary<string, object> { {"somekey", "somevalueorobject"}}
Here is the expected output I wanted.
var toplevel = Dictionary <stirng, object> {
{"Actual1", 0},
{"Actual2", 0}
}
After .ToDictionary() --> What code is best to use to achieve
var toplevel = Dictionary <stirng, object> {
{"Actual1", 0},
{"Actual2", 0},
{"AddedDynamically",0}
}
ToDictionary outputs a Dictionary, so you can easily save the result into a variable and add to it. To ensure you're getting a string and object dictionary, though, you'll need to explicitly reference the types.
var topLevel1 = resultRows
.GroupBy(g => g["CustomerAccountType"])
.ToDictionary(
g => g.Key, // This is fine since it returns a string
g => { return g.ToList() as object; }); // Explicitlyreturn this as an object
topLevel1.Add("somekey", "somevalueorobject");
To expand nested collections, use SelectMany instead of Select
var topLevel1 = resultRows
.GroupBy(g => g["CustomerAccountType"])
.SelectMany(g => g.Select(v => new { Key = g.Key, Value = v })) // This creates an anonymous type for use further into the query
.ToDictionary(
g => g.Key,
g => g.Value);
You can't do this, for multiple reasons.
Primarily, because ToDictionary returns a new dictionary object (which you then assign). Calling Add on this returns void, so you can't do the assignment.
Basically, you have to leave your first line alone. To do the merge, you need to do a foreach. This part of the question has been discussed before at: Merging dictionaries in C#
Basically you end up with a loop:
foreach (KeyValuePair<string, object> kvp in secondDictionary)
topLevel1.Add(kvp.Key, kvp.Value);
Note that the code above will break on a duplicate key.
you can try something like this:
var topLevel1 = resultRows
.GroupBy(g => g["CustomerAccountType"])
.ToDictionary(
g => g.Key,
g => { return g.ToList() as object; }).Union(new Dictionary<string, object> { {"somekey", "somevalueorobject"}).ToDictionary(x=>x.Key,x=>x.Value);
If you use something like:
Dictionary<string,Dictionary<string,object>>
then you have the dictionary you are looking for ...
public class Test
{
public static void test()
{
Dictionary<string, int> d1 = new Dictionary<string, int>();
Dictionary<string, int> d2 = new Dictionary<string, int>();
d1.AddRange(d2);
}
}
public static class Extensions
{
public static Dictionary<K, V> AddRange<K, V>(this Dictionary<K, V> d1, Dictionary<K, V> d2)
{
foreach (var kv in d2)
{
d1[kv.Key] = kv.Value;
}
return d1;
}
}
Using this extension function will:
not fail upon duplicate key
return the starting dictionary so you have a fluent interface
I have this structure:
static Dictionary<int, Dictionary<int, string>> tasks =
new Dictionary<int, Dictionary<int, string>>();
it looks like that
[1]([8] => "str1")
[3]([8] => "str2")
[2]([6] => "str3")
[5]([6] => "str4")
I want to get from this list all of the [8] strings, meaning str1 + str2
The method should look like the following:
static List<string> getTasksByNum(int num){
}
How do I access it?
With LINQ, you can do something like:
return tasks.Values
.Where(dict => dict.ContainsKey(8))
.Select(dict => dict[8])
.ToList();
While this is elegant, the TryGetValue pattern is normally preferable to the two lookup operations this uses (first trying ContainsKey and then using the indexer to get the value).
If that's an issue for you, you could do something like (with a suitable helper method):
return tasks.Values
.Select(dict => dict.TryGetValueToTuple(8))
.Where(tuple => tuple.Item1)
.Select(tuple => tuple.Item2)
.ToList();
Just iterate over all values of the first hierarchy level and use TryGetValue on the second level:
var result = new List<string>();
foreach(var inner in tasks.Values)
{
string tmp;
if(inner.TryGetValue(yourKey, out tmp)
result.Add(tmp);
}
This solution has a major advantage over all other solutions presented so far:
It actually uses the dictionaries of the second hierarchy level as a dictionary, i.e. the part inside the foreach loop is O(1) instead of O(n) as with all other solutions.
Check this function:
tasks.
Where(task => task.Value.ContainsKey(8)).
Select(task => task.Value[8]);
Daniel's solution is probably best, since it's easier to understand. But it's possible to use TryGetValue in a linq approach, too:
return tasks.Values
.Select(dictionary => {
string task;
var success = dictionary.TryGetValue(yourKey, out task);
return new { success, task };
})
.Where(t => t.success)
.Select(t => t.task)
.ToList();
Are you building tasks ?
And if I'm guessing right it's tasks[task_id]([cpu] => "task_name");
I would advice you also build cpu_tasks[cpu]([task_id] => "task_name);
static Dictionary<int, Dictionary<int, string>> cpu_tasks
It would require some more maintenance but would give you a faster run on this specific function.
Dictionary<int, Dictionary<int, string>> tasks = new Dictionary<int, Dictionary<int, string>>();
List<string> strings = new List<string>();
foreach(var dict in tasks.Values)
{
if(dict.ContainsKey(8))
strings.Add(dict[8]);
}
Dictionary<int, Dictionary<int, string>> tasks = new Dictionary<int, Dictionary<int, string>>();
var result = string.Empty;
//more human-readable version
var searchValue = 8;
foreach (var task in tasks)
{
if (task.Value.ContainsKey(searchValue))
result += task.Value[searchValue];
}
//one-line version
result = tasks.ToList().Aggregate(string.Empty, (a, kvp) => a += kvp.Value.ContainsKey(searchValue) ? kvp.Value[searchValue] : string.Empty);
This is kind-of related to this question, on how to merge two dictionaries in C#. An elegant Linq solution is presented, which is cool.
However, that question relates to Dictionary<Object1, Object2>, whereas I have a dictionary where the value is a List<Object2>.
I am looking for a solution for merging a Dictionary<Object1, List<Object2>>, with the following requirements:
If Dictionary1 contains the same key as Dictionary2, then their List<Object2> lists should be combined. You would end up with a new key-value-pair with the shared key, and the combined lists from the two dictionaries.
If Dictionary1 contains a key that Dictionary2 doesn't then the List<Object2> list from Dictionary1 should become the value, and vice versa.
This may not be possible in Linq, or it may be worth writing it out longhand with for loops and the like, but it would be nice to have an elegant solution.
I would suggest creating your own extension method. It will be more efficient and easier to modify.
public static void MergeDictionaries<OBJ1, OBJ2>(this IDictionary<OBJ1, List<OBJ2>> dict1, IDictionary<OBJ1, List<OBJ2>> dict2)
{
foreach (var kvp2 in dict2)
{
// If the dictionary already contains the key then merge them
if (dict1.ContainsKey(kvp2.Key))
{
dict1[kvp2.Key].AddRange(kvp2.Value);
continue;
}
dict1.Add(kvp2);
}
}
The difficulty is dealing with the merging of key conflicts.
If we start by flattening all the input dictionaries using SelectMany, we can group together the elements by their key.
var result = dictionaries
.SelectMany(dict => dict)
.GroupBy(kvp => kvp.Key)
The result set contains groups where each group's key is a key from the original dictionaries, and the contents of the group are an IEnumerable<List<T>> of the lists with the same key. From these groups, we can merge all List<T> into a single IEnumerable<T> using a Select transformation with SelectMany.
var result = dictionaries
.SelectMany(dict => dict)
.GroupBy(kvp => kvp.Key)
.Select(grp => new { Key = grp.Key, Items = grp.SelectMany(list => list)})
We can then get a dictionary from this using a ToDictionary transformation, converting the IEnumerable<T> back to a List<T>.
var result = dictionaries
.SelectMany(dict => dict)
.GroupBy(kvp => kvp.Key)
.Select(grp => new { Key = grp.Key, Items = grp.SelectMany(list => list)})
.ToDictionary(kip => kip.Key, kip => new List<T>(kip.Items));
Updated in response to comment
You can populate dictionaries however you like. I have assumed it is a type which implements IEnumerable<IDictionary<TKey, List<T>>> for a TKey and T of your choosing.
The simplest way would be using a List<T> as follows:
List<IDictionary<TKey, List<T>>> dictionaries
= new List<IDictionary<TKey, List<T>>>();
dictionaries.Add(dictionary1); // Your variable
dictionaries.Add(dictionary2); // Your variable
// Add any other dictionaries here.
// Code as above!
You just need to change item merging part in solution to the previous problem.
For object we have this:
.ToDictionary(group => group.Key, group => group.First())
i.e. for duplicated items, just take the first one.
But we could use this:
.ToDictionary(group => group.Key, group => group.SelectMany(list => list).ToList());
to concatenate lists.
So, the final expression would be
var result = dictionaries.SelectMany(dict => dict)
.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key,
group => group.SelectMany(list => list).ToList());
You could try a different merging expression if you need some extra list merging logic (e.g. only merge distinct items)
I'll be the first to admit that this is not all that pretty but this works for me.
var d1 = new Dictionary<string, List<string>>();
var d2 = new Dictionary<string, List<string>>();
d1["test"] = new List<string>() { "Stockholm", "Motala" };
d1["more"] = new List<string>() { "numerous", "populous", "bigger", "plentiful" };
d2["test"] = new List<string>() { "Washington", "Charlottesville" };
d2["less"] = new List<string>() { "insufficient", "small", "imperceptible" };
var intersect = (from key in d1.Keys.Intersect(d2.Keys) select new { Key = key, Value = new List<string>(d1[key].Concat(d2[key])) }).ToDictionary(d => d.Key, d => d.Value);
var merged = d1.Concat(d2).Where(d => !intersect.Keys.Contains(d.Key)).Concat(intersect).ToDictionary(d => d.Key, d => d.Value);