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);
Related
I have 2 dictionaries the first one has a key that are match multiple duplicate values in the second one:
var firstDic = new Dictionary<string, string>();
firstDic.Add("123", "Value1");
firstDic.Add("456", "Value2");
firstDic.Add("789", "Value3");
firstDic.Add("000", "Value4");
var secondDic = new Dictionary<string, string>();
secondDic.Add("5d866ac0-abab-46c9-a951-e7b0cf25dc72", "123");
secondDic.Add("217ddba3-977f-45b8-a7f6-80c6fbcef16e", "123");
secondDic.Add("99867f65-22c1-4b6c-b1bb-3fa73af317a9", "456");
secondDic.Add("203f9cf7-98f8-40fc-a4ba-da3a62c7d795", "456");
secondDic.Add("9bdafb4c-4d5a-4c87-8b9d-d9b98473390a", "789");
secondDic.Add("d3a245f0-cc5b-4c08-aaff-475d64e27e8d", "000");
I have a foreach in which i`m iterating over every key in the first dictionary
foreach (var item in firstDic)
{
//It`s only taking the first match in the secondDic
var myKey = secondDic.FirstOrDefault(x => x.Value == item.Key);
}
Is it possible to extract all the keys from the second dictionary(secondDic)that has the values matching from the first dictionary key and store them in some data structure.
Tnx in advice!
Then you don't need to use FirstOrDefault(), just use the Where() which will return a collection back :
var myKeys = secondDic.Where(x => x.Value == item.Key);
and then you can iterate over it:
foreach(var item in myKeys)
{
}
or do whatever business logic needs to be applied.
This may help
var result = secondDic.Where(s => firstDic.Any(f => f.Key == s.Value))
.Select(s => s.Key)
.ToList();
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 list acd with key value pair.
var acd = zebra.Where(v => v.Key.StartsWith("alpha"));
KEY, VALUE
alphaABC, TOP323
alphaBCD, BIG456
alphaDEF, TOP323
What i would want is to get only One Key (Any) from multiple keys which have same values.
In this case 1 and 3 have same values.
I would like to get a new list like below:
alphaABC, TOP323
alphaBCD, BIG456
Basically unique Values only. Any Help ?
List<KeyValuePair<string, string>> data = new List<KeyValuePair<string, string>>()
{ new KeyValuePair<string, string>("ABC", "TOP323"),
new KeyValuePair<string, string>("BCD", "BIG456"),
new KeyValuePair<string, string>("DEF", "TOP323") };
var result = (from d in data
group d by d.Value
into g
select new
{
row = g.FirstOrDefault()
}).ToList();
var items = zebra
.Where(v => v.Key.StartsWith("alpha"))
.GroupBy(pair => pair.Value)
.Select(group => group.First())
.ToArray();
foreach(var item in items)
Console.WriteLine("{0}, {1}", item.Key, item.Value);
Use a Dictionary<TKey,TValue>
var dict = new Dictionary<string,string>(zebra.Count);
foreach (KeyValuePair pair in zebra) {
if (!dict.ContainsKey(pair.Value)) {
dict.Add(pair.Value, pair.Key);
}
}
Note that we invert the meaning of key and value here. We use pair.Value as key in the dict, since we want unique values.
As an alternative you could also declare the dictionary as Dictionary<string,KeyValuePair<string,string>> and add like this
dict.Add(pair.Value, pair);
I need the Key/Value stuff from a Dictionary. What I do not need is that it does not allow duplicate Keys.
Regex template = new Regex(#"\{(?<key>.+?)\}(?<value>[^{}]*)");
IDictionary<string, string> dictionary = template.Matches(MyString)
.Cast<Match>()
.ToDictionary(x => x.Groups["key"].Value, x => x.Groups["value"].Value);
How can I return the Dictionary allowing duplicate keys?
Use the Lookup class:
Regex template = new Regex(#"\{(?<key>.+?)\}(?<value>[^{}]*)");
ILookup<string, string> dictionary = template.Matches(MyString)
.Cast<Match>()
.ToLookup(x => x.Groups["key"].Value, x => x.Groups["value"].Value);
EDIT: If you expect to get a "plain" resultset (e.g. {key1, value1}, {key1, value2}, {key2, value2} instead of {key1, {value1, value2} }, {key2, {value2} }) you could get the result of type IEnumerable<KeyValuePair<string, string>>:
Regex template = new Regex(#"\{(?<key>.+?)\}(?<value>[^{}]*)");
ILookup<string, string> dictionary = template.Matches(MyString)
.Cast<Match>()
.Select(x =>
new KeyValuePair<string, string>(
x.Groups["key"].Value,
x.Groups["value"].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);