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

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

Related

Getting more then one value in the same time from Dictionary

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

Use linq expression to filter a dictionary with a list of keys

I have a dictionary containing all users with their corresponding age.
Dictionary<string,int> AllUsers;
I have a list of specific users.
List<String> Users;
I would like to filter the first dictionary AllUsers with only the users who have their name in the SpecificUsers list.
I have done something manually with loops but I would like to use linq expression but I am not very familiar with them.
Thanks in advance for your help
You could filter Users:
Users.Where(i => AllUsers.ContainsKey(i)).Select(i => new { User = i, Age = AllUsers[i] });
The major benefit of this is that you're using the indexed AllUsers to do the filtering, so your total computational complexity only depends on the amount of users in Users (Dictionary.Contains is O(1)) - the naïve approaches tend to be Users * AllUsers.
If you want a dictionary on output, it's as simple as replacing the .Select(...) above with
.ToDictionary(i => i, i => AllUsers[i])
It might work
var newdict = AllUsers.Where(x => Users.Contains(x.Key))
.ToDictionary(val => val.Key, val => val.Value);
it will create new dictionary (cause linq is for querying not updating) with all the users from dictionary that are on the Users list. You need to use ToDictionary to actualy make it dictionary.
EDIT:
As #Rawling said it would be more performant to filter on Dictionary rather than on list. Solution to achieve that is present in #Luaan answer (I won't copy it as some do)
You can use a join() method to actually join the two collections. It allows us to get what you need with a single line of linq.
var allUsers = new Dictionary<string, int>();
allUsers.Add("Bob", 10);
allUsers.Add("Tom", 20);
allUsers.Add("Ann", 30);
var users = new List<string>();
users.Add("Bob");
users.Add("Tom");
users.Add("Jack");
var result = allUsers.Join(users, o => o.Key, i => i, (o, i) => o);
foreach(var r in result)
{
Console.WriteLine(r.Key + " " + r.Value);
}
It will output the following in the console:
Bob 10
Tom 20
Only the names that appears in both collection will be available in the result collection
There are multiple ways to do this
You can use this using where keyword
var result= yourDictionary.Where(p=> yourList.Contains(p.Key))
.ToDictionary(p=> p.Key, p=> p.Value);
But if you have lot of entries its better to use HashSet
var strings = new HashSet<string>(yourList);
var result= yourDictionary.Where(p=> strings.Contains(p.Key))
.ToDictionary(p=> p.Key, p=> p.Value);
using JOIN
var query =
from kvp in yourDictionary
join s in yourList on kvp.Key equals s
select new { kvp.Key, kvp.Value };
With the help of the following useful function
public static class Extensions
{
public static KeyValuePair<TKey,TValue>? Find<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key)
{
TValue value;
return source.TryGetValue(key, out value) ? new KeyValuePair<TKey, TValue>(key, value) : (KeyValuePair<TKey, TValue>?)null;
}
}
here is IMO the optimal solution (uses single lookup per key and does not introduce closure):
var filteredUsers = Users.Select(AllUsers.Find)
.Where(item => item.HasValue)
.ToDictionary(item => item.Value.Key, item => item.Value.Value);

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

Merging Dictionary containing a List in C#

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

Categories

Resources