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
Related
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
Given:
class KeyType1{...}
class KeyType2{...}
class ValueType{...}
public KeyType2 Lookup(KeyType1 value)
{
// Returns a unique value of KeyType2 or otherwise, null
}
Is there a neat LINQ way to transform:
IDictionary<KeyType1,ValueType> ==> IDictionary<KeyType2,ValueType>
The following important points:
No 2 values of type KeyType1 map to the same KeyType2
edit It is possible Lookup may return null i.e. the mapping may not be complete. In such cases, these entries should be omitted from the new dictionary
ok, assuming Lookup is a function that returns a reference type that could be null.
b = a
.Select(p => new { Key = Lookup(p.Key), p.Value }))
.Where(p => p.Key != null)
.ToDictionary(p => p.Key, p => p.Value);
You can use Where firstly to filter the source, then use ToDictionary extension specifying the new key and the value:
var dictionary1 = new Dictionary<KeyType1, ValueType>();
var dictionary2 = dictionary1.Where(kv => Lookup(kv.Key) != null)
.ToDictionary(kv => Lookup(kv.Key), kv => kv.Value);
If you want to compute the key for those cases where Lookup is null you can do this:
dictionary1.ToDictionary(kv => Lookup(kv.Key) ?? ComputeNewKey(kv.Key),
kv => kv.Value);
To avoid to need Lookup twice you can firstly create a Dictionary<KeyType1, KeyType2> to map the old keys to the new ones:
IDictionary<KeyType1,KeyType2> keyMap = sourceDictionary.Keys.ToDictionary(key => key, key => Lookup(key));
This keyMap now may contain null values for some source keys. So we filter it with Where and then combine them with the source dictionary to create your new dictionary:
IDictionary<KeyType2,ValueType> target = keyMap.Where(kvp => kvp.Value != null).
ToDictionary(kvp => kvp.Value, sourceDictionary[kvp.Key]);
Dictionary<string, string> StringDict = new Dictionary<string, string>();
Dictionary<int, string> IntDict = new Dictionary<int, string>();
StringDict.ToList().ForEach(D =>
{
IntDict.Add(Convert.ToInt32(D.Key), D.Value);
});
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);
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);
I have a C# dictionary, Dictionary<Guid, MyObject> that I need to be filtered based on a property of MyObject.
For example, I want to remove all records from the dictionary where MyObject.BooleanProperty = false. What is the best way of acheiving this?
If you don't care about creating a new dictionary with the desired items and throwing away the old one, simply try:
dic = dic.Where(i => i.Value.BooleanProperty)
.ToDictionary(i => i.Key, i => i.Value);
If you can't create a new dictionary and need to alter the old one for some reason (like when it's externally referenced and you can't update all the references:
foreach (var item in dic.Where(item => !item.Value.BooleanProperty).ToList())
dic.Remove(item.Key);
Note that ToList is necessary here since you're modifying the underlying collection. If you change the underlying collection, the enumerator working on it to query the values will be unusable and will throw an exception in the next loop iteration. ToList caches the values before altering the dictionary at all.
Since Dictionary implements IEnumerable<KeyValuePair<Key, Value>>, you can just use Where:
var matches = dictionary.Where(kvp => !kvp.Value.BooleanProperty);
To recreate a new dictionary if you need it, use the ToDictionary method.
You can simply use the Linq where clause:
var filtered = from kvp in myDictionary
where !kvp.Value.BooleanProperty
select kvp
public static Dictionary<TKey, TValue> Where<TKey, TValue>(this Dictionary<TKey, TValue> instance, Func<KeyValuePair<TKey, TValue>, bool> predicate)
{
return Enumerable.Where(instance, predicate)
.ToDictionary(item => item.Key, item => item.Value);
}
Here is a general solution, working not only for boolean properties of the values.
Method
Reminder: Extension methods must be placed in static classes. Dont forget the using System.Linq; statement at the top of the source file.
/// <summary>
/// Creates a filtered copy of this dictionary, using the given predicate.
/// </summary>
public static Dictionary<K, V> Filter<K, V>(this Dictionary<K, V> dict,
Predicate<KeyValuePair<K, V>> pred) {
return dict.Where(it => pred(it)).ToDictionary(it => it.Key, it => it.Value);
}
Usage
Example:
var onlyWithPositiveValues = allNumbers.Filter(it => it.Value > 0);
I added the following extension method for my project which allows you to filter an IDictionary.
public static IDictionary<keyType, valType> KeepWhen<keyType, valType>(
this IDictionary<keyType, valType> dict,
Predicate<valType> predicate
) {
return dict.Aggregate(
new Dictionary<keyType, valType>(),
(result, keyValPair) =>
{
var key = keyValPair.Key;
var val = keyValPair.Value;
if (predicate(val))
result.Add(key, val);
return result;
}
);
}
Usage:
IDictionary<int, Person> oldPeople = personIdToPerson.KeepWhen(p => p.age > 29);
Hope it fit your needs.
Dictionary<string, string> d = new Dictionary<string, string>();
d.Add("Product", "SELECT * FROM PRODUCT");
d.Add("Order", "SELECT * FROM ORDER");
d.Add("Customer", "SELECT * FROM CUSTOMER");
var x = d.Where(kvp => kvp.Key == "Order")
.ToDictionary(item => item.Key, item => item.Value)
.FirstOrDefault();
if(x.Key != null)
{
Console.WriteLine($"{x.Value}"); //this should return "SELECT * FROM ORDER"
}