I have two dictionary in C# 6.0 and I'd like to merge them in a smart way.
Take the first dictionary foo as:
var foo = new Dictionary<string, string>
{
{"a", "10"},
{"b", "20"},
{"c", "30"},
};
And the second dictionary bar as:
var bar = new Dictionary<string, string>
{
{"a", "333"},
{"e", "444"},
{"f", "555"},
};
I'd like to merge them in one dictionary with this logic:
If a key is in foo but not in bar ignore it in the new dictionary
If a key is in bar but not in foo, take it in the new dictionary
if a key is in both foo and bar, take the value of foo in the new dictionary
Here is my expected result:
var result = new Dictionary<string, string>
{
{"a", "10"}, //this comes from foo
{"e", "444"}, //this comes from bar
{"f", "555"}, //this comes from bar
};
Is there a smart way to handle this problem without forloop (LINQ expressions are fine)?
You could use HashSet<T> methods and LINQ:
1)
var fooKeys = new HashSet<string>(foo.Keys);
var barKeys = new HashSet<string>(bar.Keys);
fooKeys.IntersectWith(barKeys); // remove all from fooKeys which are not in both
barKeys.ExceptWith(fooKeys); // remove all from barKeys which are remaining in fooKeys and also in barKeys
Dictionary<string, string> result = fooKeys
.Select(fooKey => new KeyValuePair<string, string>(fooKey, foo[fooKey]))
.Concat(barKeys.Select(bKey => new KeyValuePair<string, string>(bKey, bar[bKey])))
.ToDictionary(kv => kv.Key, kv => kv.Value);
This is safe because both exclude each other. It's also very efficient since these HashSet methods have O(n) complexity with two sets.
If it's not understandable in your opinion, maybe you like this more:
2)
var inBoth = from kv1 in foo
join kv2 in bar
on kv1.Key equals kv2.Key
select kv1;
var onlyInBar = bar.Keys.Except(foo.Keys)
.Select(b => new KeyValuePair<string, string>(b, bar[b]));
Dictionary<string, string> result = new Dictionary<string, string>();
foreach (var kv in inBoth.Concat(onlyInBar))
result.Add(kv.Key, kv.Value);
The first query uses a join(more readable in query-syntax) which only returns keyvalue-pairs from the first dictionary where the key also exists in the second dictionary. The second query uses Enumerable.Except to exclude all from the second dictionary which are in the first. Both, Enumerable.Join and Enumerable.Except use sets under the hood so they are very efficient.
It's worth noting that due to LINQ's deferred execution both queries are executed only at the foreach (var kv in inBoth.Concat(onlyInBar)) and not before.
Probably the easiest and most readable approach, a "LINQ left-outer-join":
3)
KeyValuePair<string, string> defaultPair = default(KeyValuePair<string, string>);
var query = from barKv in bar
join fooKv in foo
on barKv.Key equals fooKv.Key into gj_bf
from bf in gj_bf.DefaultIfEmpty()
select bf.Equals(defaultPair) ? barKv : bf;
foreach (var kv in query)
result.Add(kv.Key, kv.Value);
You can use GroupJoin like this:
var res =
bar
.GroupJoin(
foo,
kvp => kvp.Key,
kvp => kvp.Key,
(kvp, g) => new KeyValuePair<string, string>(kvp.Key, g.FirstOrDefault().Value ?? kvp.Value))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
The trick here is to GroupJoin bar with foo! this way, everything from bar will appear in the final result and for same keys the joined result will be an IEnumerable of matched results from second collection which in your case is foo and since it is a Dictionary so the matched results will contain only one element and all you need to do is to get its value. In case of no match (items in bar but not in foo) the matched results collection will be empty, so FirstOrDefault() will return the default value of KeyValuePair<string, string> with Key and Value both set to null. So in this case we just get the Value from our first collection (in your case bar).
Your logic can be simplified as:
The result will contain all keys from bar, with value taken from foo if exists, otherwise from bar.
which translates to something like this:
var result = bar.ToDictionary(barItem => barItem.Key, barItem =>
foo.ContainsKey(barItem.Key) ? foo[barItem.Key] : barItem.Value);
or a bit longer, but more optimal:
var result = bar.ToDictionary(barItem => barItem.Key, barItem =>
{ string fooValue; return foo.TryGetValue(barItem.Key, out fooValue) ? fooValue : barItem.Value; });
A (simple) Linq solution:
var newDict = new Dictionary<string, string>();
var toIncludeFromFoo = bar.Keys.Intersect(foo.Keys).ToList();
toIncludeFromFoo.ForEach(x => newDict [x] = foo[x]);
var toAddFromBar = bar.Keys.Except(foo.Keys).ToList();
toAddFromBar.ForEach(x => newDict [x] = bar[x]);
Related
I have a list of tuples with dual values:
List<Tuple<string, string>> Descriptions;
And I keep adding content to this, like so:
Descriptions.Add (new Tuple<string, string> ("max", "some description"));
Descriptions.Add (new Tuple<string, string> ("joe", "some description"));
Descriptions.Add (new Tuple<string, string> ("jane", "some description"));
Descriptions.Add (new Tuple<string, string> ("max", "some other description"));
I want to retrieve a list using Linq where Item1 in the tuple is a certain value like, say, "max". I could use this code:
var s = Descriptions.Where (x => x.Item1 == "max");
but this will assign s a list of tuples, which I don't want. I just want a list of the description strings, that is, it should return a list<string> with all the descriptions associated with the Item1 string "max".
Use Select:
var s = Descriptions.Where (x => x.Item1 == "max").Select(y => y.Item2);
That will return an IEnumerable<string>. If you want a list, you need to also add ToList at the end:
var s = Descriptions.Where (x => x.Item1 == "max").Select(y => y.Item2).ToList();
Or you can use Query syntax:
var s = from d in Descriptions
where d.Item1 == "max"
select d.Item2;
That's the same as the first option. In fact, the compiler will translate query syntax to linq's extension methods.
After Where() you can use Select() method to get only description which in your case will be Item2 of Tuple and you would need to do like:
var s = Descriptions.Where(x => x.Item1 == "max")
.Select(x=>x.Item2); // projects only Description
This will return you all elements in form of IEnumerable<string> which have Item1 having value "max" and if you really want to get it as List<string>, then you can add ToList() method call at end.
Hope it helps.
if you don't use the other solutions try using a dictionary instead of a list of tuples. by the looks of your content this is more what you might want (only if your names are unique).
Dictionary<string, string> NameDesc = new Dictionary<string, string>();
NameDesc.Add("max", "desc1");
NameDesc.Add("tim", "desc2");
var description = NameDesc["max"];
var maxExists = NameDesc.ContainsKey("max");
I have this
foreach (KeyValuePair<string, Object> tempData in tempList.ToDictionary(x => x.Key, y => y.Value))
{
tempData["fahrzeugA"] = "s";
}
But using tempData["fahrzeugA"] = "s"; will not work.
I get:
Cannot apply indexing with [] to an expression of type
'System.Collections.Generic.KeyValuePair'
What is the correct syntax if I have an existing key fahrzeugA, which I want to alter ?
You can apply this :
var tempList = new List<Test>();
var dic = tempList.ToDictionary(x => x.Key, y => y.Value);
foreach (var tempData in dic)
{
dic[tempData.Key] = "s";
}
You can't change the key value pair since it is an immutable struct. The only way to change it is to create a new instance. That instance would live independent from the dictionary.
If you want to change the value in the dictionary, use the indexer property on the dictionary to change the value.
And even then, the dictionary will go out of scope immediately, so there is no use setting it. It won't affect the original list.
Check KeyValuePair.Value Property. It's readonly and can't be altered.
ToDictionary creates a new object. You can't alter original object by accessing its elements' value.
You have to remove this specific item from original list and add new item of the same key back.
var removeIndex = tempList.FindIndex(kp => kp.Key == "fahrzeugA");
tempList.RemoveAt(removeIndex);
tempList.Add(new KeyValuePair<string, string>("fahrzeugA", "s"));
If there are multiple "fahrzeugA" items (it's valid in list but not valid in dictionary), use RemoveAll instead.
If your tempList is List<KeyValuePair<string, Object>> type:
for (var i = 0; i < tempList.Count; ++i) {
if (tempList[i].Key == "fahrzeugA") {
tempList[i] = new KeyValuePair<string, object> ("fahrzeugA", "s"); // KeyValuePair<string, object> might be changed with your own type if you use something else.
break; // If you want to modify only first KeyValuePair.
}
}
If you have successfully turned your tempList into a dictionary, there can only be one "fahrzeugA" (since all keys must be unique), so looping makes no sense.
You should be able to just say:
var dictionary = tempList.ToDictionary(x => x.Key, y => y.Value);
dictionary["fahrzeugA"] = "s";
If you don't want to create the dictionary in the first place, you could do this:
var matchingKeyValuePair = tempList.SingleOrDefault(x => x.Key == "fahrzeugA");
if (matchingKeyValuePair != null) matchingKeyValuePair.Value = "s";
If you are using a list of .NET KeyValuePair<TKey, TValue>, which is an immutable struct, you can replace the value with a new KeyValuePair, like this:
var matchingIndex = tempList.FindIndex(x => x.Key == "fahrzeugA");
if (matchingIndex >= 0)
tempList[matchingIndex] = new KeyValuePair<string, string>("fahrzeugA", "s");
Note, this assumes that you only have one item with a key of "fahrzeugA".
From a datatable I am fetching the value and putting in a Dictionary<string,string>:
Dictionary<string, string> mydic= new Dictionary<string, string>();
my datatable for ex is
Value RowOrder
page1 01
page2 00
page3 00
I am using LINQ to fetch the RowOrder according to value given and putting into mydic:
string id = (from DataRow dr in table3.Rows where (string)dr["Value"] == formula
select (string)dr["RowOrder"]).FirstOrDefault();
mydic.Add(id,Value);
If I run this, error is showing:
"An item with the same key has already been added."
How to overcome this. I want page1, page2, page3 should be added with values 01, 00, 00 respectively
You need to check whether the dictionary already has the key before adding to it:
if(!mydic.ContainsKey(id))
{
mydic.Add(id, Value);
}
A dictionary cannot contain two items with the same key, if you are expecting duplicate id values you need to consider using a different data structure.
Perhaps a List<Tuple<string, string>>?
Check if key exists before adding
if (mydic.ContainsKey(id))
mydic[id] = Value; // or throw exception
else
mydic.Add(id, Value);
BTW if you want to convert your DataTable to Dictionary<string, string> with RowOrder as key, and first (or last) Value as value, you can use LINQ:
var mydic = table3.AsEnumerable()
.GroupBy(r => r.Field<string>("RowOrder"))
.Select(g => g.First()) // or last to use last value for key
.ToDictionary(r => r.Field<string>("RowOrder"),
r.Field<string>("Value"));
You can use GroupBy, here a single line approach using Linq-To-DataSet:
Dictionary<string, string> mydic = table3.AsEnumerable()
.GroupBy(row => row.Field<string>("Value"))
.ToDictionary(grp => grp.Key, grp => grp.First().Field<string>("RowOrder"));
Use
mydic[id] = Value;
instead of mydic.Add();
The Add method should be used if you want to ensure only one item with a given key is inserted.
Note that this overwrite the previously written value.
If you want to have more items with the same key you should use a
Dictionary<string, IList<string>>
or some other datastructure I don't know of but I would be very glad to hear about since I used that kind of dictionary more than once
You may use a Lookup<string,string>:
List<Tuple<string, string>> tuples = new List<Tuple<string, string>>();
tuples.Add(new Tuple<string, string>("01", "page1"));
tuples.Add(new Tuple<string, string>("00", "page2"));
tuples.Add(new Tuple<string, string>("00", "page3"));
var lookup = tuples.ToLookup(t => t.Item1,t=> t.Item2 );
And you may use it like:
var result = lookup["00"];
foreach (var item in result)
{
Console.WriteLine(item);
}
Which outputs:
page2
page3
List<KeyValuePair<String, String> myList = new List<KeyValuePair<String, String>>();
myList.Add(new KeyValuePair<String, SelectList>("theKey", "FIND THIS!"));
How can I retrieve "FIND THIS!" from myList knowing only theKey? This attempt is not working.
String find = myList.Where(m => m.Key == "theKey");
Coming from other languages, I've always had the possibility to search in big associative arrays and retrieve values like this: array[key] = value;
How can I do this in C#?
Instead of List<KeyValuePair>, use Dictionary<string, SelectList> and then you can access it like :
array[key] = value;
You can use Dictionary like:
Dictionary<String, SelectList> dictionary= new Dictionary<String, SelectList>();
dictionary.Add("theKey", "FIND THIS!");
Console.WriteLine(dictionary["theKey"]);
You are probably looking for the Dictionary<TKey, TValue>:
Dictionary<string, string> myDict = new Dictionary<string, string>();
myDict.Add("theKey", "FIND THIS!");
now you can find the value via the key:
string value = myDict["theKey"];
You can change the value in this way:
myDict["theKey"] = "new value"; // works even if the key doesn't exist, then it will be added
Note that the keys must be unique.
How about Dictionary ?
IDictionary<String, String> foo = new Dictionary<String, String>();
foo.Add("hello","world");
now you can use []
foo["Hello"];
however with C#
string value;
if(foo.TryGetValue("Hello" , out value)){
// now you have value
}
is much more preferable and safer.
As mentioned in other answers you should use a Dictionary for this.
However, the reason your line String find = myList.Where(m => m.Key == "theKey"); is not working is that myList.Where(m => m.Key == "theKey"); will return a KeyValuePair. If you just want the value you could try:
String find = myList.Where(m => m.Key == "theKey").Single().Value;
or if you need to check for nulls then maybe:
var findKeyValue = myList.Where(m => m.Key == "theKey").SingleOrDefault();
if(findKeyValue != null)
{
var find = findKeyValue.Value;
}
You can also use the following snippet (in which case you'll either have the value or null)
var find = myList.Where(m => m.Key == "theKey").Select(kvp => kvp.Value).SingleOrDefault();
How to convert
Dictioanry<String,List<String>> into Dictionary<String,String>
i'm having a dictionary like
Dictioanry<String,List<String>>dictOne=new Dictionary<String,List<String>>();
and which containg
Key(String) Value(List<String>)
A a1,a2
B b1,b2
C c1
i need to convert the "dictOne" into
Dictionary<String,String> dictReverse=new Dictionary<String,String>()
So the result will be like
Key(String) Value(String)
a1 A
a2 A
b1 B
b2 B
c1 C
is there any way to do this using LINQ
Thanks in advance
Update: As others have noted, in order for a dictionary to be truly "reversible" in this way, the values in your List<string> objects need to all be unique; otherwise, you cannot create a Dictionary<string, string> with an entry for every value in your source dictionary, as there would be duplicate keys.
Example:
var dictOne = new Dictionary<string, List<string>>
{
{ "A", new List<string> { "a1", "a2" } },
{ "B", new List<string> { "b1", "b2" } },
{ "C", new List<string> { "c1", "a2" } } // duplicate!
};
You have (at least) two options for dealing with this.
Option 1: Throw on duplicates
You may want to ensure that every element in every List<string> is, in fact, unique. In this case, a simple SelectMany with a ToDictionary will accomplish what you need; the ToDictionary call will throw an ArgumentException on encountering a duplicate value:
var dictTwo = dictOne
.SelectMany(kvp => kvp.Value.Select(s => new { Key = s, Value = kvp.Key }))
.ToDictionary(x => x.Key, x => x.Value);
The most generic way (that comes to mind) to abstract this functionality into its own method would be to implement an extension method that does this for any IDictionary<T, TEnumerable> implementation where TEnumerable implements IEnumerable<TValue>:
// Code uglified to fit within horizonal scroll area
public static Dictionary<T2, T1> ReverseDictionary<T1, T2, TEnumerable>(
this IDictionary<T1, TEnumerable> source) where TEnumerable : IEnumerable<T2>
{
return source
.SelectMany(e => e.Value.Select(s => new { Key = s, Value = e.Key }))
.ToDictionary(x => x.Key, x => x.Value);
}
The ugly proliferation of generic type parameters in the above method is to allow for types other than strictly Dictionary<T, List<T>>: it could accept a Dictionary<int, string[]>, for example, or a SortedList<string, Queue<DateTime>> -- just a couple of arbitrary examples to demonstrate its flexibility.
(A test program illustrating this method is at the bottom of this answer.)
Option 2: Skip duplicates
If duplicate elements in your List<string> values is a realistic scenario that you want to be able to handle without throwing an exception, I suggest you take a look at Gabe's excellent answer for an approach that uses GroupBy (actually, Gabe also provides a flexible approach that can cover either of these two cases based on a selector function; however, if you definitely want to throw on a duplicate, I'd still suggest the above approach, as it should be somewhat cheaper than using GroupBy).
Example program
Here's a little test program illustrating Option 1 above on a Dictionary<string, List<string>> with no duplicate elements in its List<string> values:
var dictOne = new Dictionary<string, List<string>>
{
{ "A", new List<string> { "a1", "a2" } },
{ "B", new List<string> { "b1", "b2" } },
{ "C", new List<string> { "c1" } }
};
// Using ReverseDictionary implementation described above:
var dictTwo = dictOne.ReverseDictionary<string, string, List<string>>();
foreach (var entry in dictTwo)
{
Console.WriteLine("{0}: {1}", entry.Key, entry.Value);
}
Output:
a1: A
a2: A
b1: B
b2: B
c1: C
// Associates each key with each of its values. Produces a sequence like:
// {A, a1}, {A, a2}, {B, b1}, {B, b2}, {C, c1}
var kvps = from kvp in dictOne
from value in kvp.Value
select new { Key = kvp.Key, Value = value };
// Turns the sequence into a dictionary, with the old 'Value' as the new 'Key'
var dictReverse = kvps.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
Of course, each key in the original dictionary must be associated with a unique set of values, and no key must be associated with values that are also associated with other keys.
Also bear in mind that Dictionary<K, V> does not define any sort of enumeration order. You can use the Enumerable.OrderBy method to enumerate the resulting dictionary in the appropriate order.
In the event that you would end up with duplicate keys in your result dictionary, you would have to pick a single one of those keys. Here's an implementation that just picks the first one it sees (using First):
var dictReverse = (from kvp in dictOne
from value in kvp.Value
group kvp.Key by value)
.ToDictionary(grp => grp.Key, grp => grp.First());
Given this input dictionary:
var dictOne = new Dictionary<string, IEnumerable<string>> {
{ "C", new List<string> { "c1", "a2" } },
{ "B", new List<string> { "b1", "b2" } },
{ "A", new List<string> { "a1", "a2" } } };
The result would be:
c1: C
a2: C
b1: B
b2: B
a1: A
As Dan points out, you may want different behavior in the case of duplicate keys. You can create this function:
public static Dictionary<V, K> Transpose<K, V>(
this Dictionary<K, IEnumerable<V>> dictOne,
Func<IEnumerable<K>, K> selector)
{
return (from kvp in dictOne
from V value in kvp.Value
group kvp.Key by value)
.ToDictionary(grp => grp.Key, grp => selector(grp));
}
Then you could call it like dictOne.Transpose(Enumerable.First) to get the above behavior, dictOne.Transpose(Enumerable.Single) to get an exception when there's a duplicate key (the behavior of other posts), dictOne.Transpose(Enumerable.Min) to pick the first one lexicographically, or pass in your own function do whatever you need.