Getting more then one value in the same time from Dictionary - c#

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

Related

Populate new dictionary from old dictionary

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

Transform a dictionary to another with a different key type

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

C# get keys and values from List<KeyValuePair<string, string>

Given a list:
private List<KeyValuePair<string, string>> KV_List = new List<KeyValuePair<string, string>>();
void initList()
{
KV_List.Add(new KeyValuePair<string, string>("qwer", "asdf"));
KV_List.Add(new KeyValuePair<string, string>("qwer", "ghjk"));
KV_List.Add(new KeyValuePair<string, string>("zxcv", "asdf"));
KV_List.Add(new KeyValuePair<string, string>("hjkl", "uiop"));
}
(NOTE: there are multiple values for the key "qwer" and multiple keys for the value "asdf".)
1) Is there a better way to return a list of all keys than just doing a foreach on the KeyValuePair List?
2) Similarly, is there a better way to return a list of all values for a given key than using a foreach?
3) And then, how about returning a list of keys for a given value?
Thanks...
// #1: get all keys (remove Distinct() if you don't want it)
List<string> allKeys = (from kvp in KV_List select kvp.Key).Distinct().ToList();
// allKeys = { "qwer", "zxcv", "hjkl" }
// #2: get values for a key
string key = "qwer";
List<string> values = (from kvp in KV_List where kvp.Key == key select kvp.Value).ToList();
// values = { "asdf", "ghjk" }
// #3: get keys for a value
string value = "asdf";
List<string> keys = (from kvp in KV_List where kvp.Value == value select kvp.Key).ToList();
// keys = { "qwer", "zxcv" }
It sounds like you would benefit from using something like:
Dictionary<string, List<string>> kvlist;
kvlist["qwer"] = new List<string>();
kvlist["qwer"].Add("value1");
kvlist["qwer"].Add("value2");
foreach(var value in kvlist["qwer"]) {
// do something
}
It would be relatively easy to create a basic mutli-value dictionary class using a Dictionary and List.
This blog post talks more about Microsoft's MultiDictionary type available via NuGet.
Well you could definitly use your LINQ. But it's not "better" (in term of performance) since looping is already fast. It is perhaps more readable (personnal preference). For all the answers below, be aware that you need to have the System.Linq namespace imported. They also return IEnumerable<T> that are lazy loaded (executed when iterated over). If you want to return a concrete list, you can call the .ToList() extension.
Is there a better way to return a list of all keys than just doing a foreach on the KeyValuePair List?
KV_List.Select(kvp => kvp.Key);
Similarly, is there a better way to return a list of all values for a given key than using a foreach?
var theKeyToLookFor = "qwer";
KV_List.Where(kvp => kvp.Key == theKeyToLookFor).Select(kvp => kvp.Value);
And then, how about returning a list of keys for a given value?
var theValueToLookFor = "asdf";
KV_List.Where(kvp => kvp.Value == theValueToLookFor)
.Select(kvp => kvp.Value)
.ToList();
For more information on LINQ, look at LINQ (Language-Integrated Query)
You can use NameValueCollection from System.Collection.Specialized namespace:
NameValueCollection KV_List = new NameValueCollection();
KV_List.Add("qwer", "asdf");
KV_List.Add("qwer", "ghjk");
KV_List.Add("zxcv", "asdf");
KV_List.Add("hjkl", "uiop");
Example of use:
string singleValue = KV_List["zxcv"]; // returns "asdf"
string[] values = KV_List.GetValues("qwer"); // returns "asdf, "ghjk"
string[] allKeys = KV_List.AllKeys;
string[] allValues = KV_List.AllKeys;
https://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection%28v=vs.110%29.aspx
1:
KV_List.Select(i => i.Key).ToList()
2:
KV_List.Where(i => i.Key == filterByKey).Select(i => i.Value).ToList()
3:
KV_List.Where(i => i.Value == filterByValue).Select(i => i.Key).ToList()
I would use ILookup<K,V> in your case. It is like a dictionary but you can get the values as IEnumerable<V> with the same key.
ILookup<string, string> lookup = KV_List.ToLookup(x => x.Key, x => x.Value);
IEnumerable<string> list = lookup["qwer"];
foreach(string str in list)
{
Console.WriteLine(str);
}
or simply
Console.WriteLine(string.Join(",", lookup["qwer"]));
Are you reading this and wondering why someone made some code have an IEnumerable<KeyValuePair<A,B>> instead of a Dictionary<A,B> but don't feel like asking and just want to get done?
if (collection == null)
return null;
return collection
.Where(z => z.Key == aThing)
.Select(z => z.Value)
.FirstOrDefault();

Group Dictionary by splitting the Key and create new Dictionary

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

Group by on a List And Return Top 1 Row

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

Categories

Resources