Replace Dictionary values C# - c#

I have two dictionaries say
Dictionary<int,string> DictA=new Dictionary<int,string>();
DictA.Add(1,"A");
DictA.Add(2,"B");
DictA.Add(3,"C");
DictA.Add(4,"D");
Dictionary<string,string> DictB=new Dictionary<string,string>();
DictB.Add("A","A1");
DictB.Add("C","C1");
DictB.Add("D","D1");
Now I need a resultant dictionary as follows
<1,"A1">
<2,"B"> //Since B is missing in Second Dictionary it should
retain its old value
<3,"C1">
<4,"D1">
I am not happy with the iterator code I wrote for this using foreach, but I am certain that there is very short and cleaner way to do this using Lambda Expressions. I am very new to them and finding them difficult to understand.
Can someone explain a good resource to learn them and use efficiently with an example.

You can use LINQ.Basically just check if the second dictionary contains the value of first dictionary as Key, if it does get the value from second dictionary, if it doesn't get the value from the first dictionary:
DictA
.Select(x => new KeyValuePair<int,string>(x.Key,
DictB.ContainsKey(x.Value)
? DictB[x.Value]
: x.Value)
.ToDictionary(x => x.Key, x => x.Value);

you can use ToDictionary to generate the dictionary the way you want.
var result = DictA.ToDictionary(a => a.Key, a => DictB.ContainsKey(a.Value) ? DictB[a.Value] : a.Value);
foreach (var item in result)
{
Console.WriteLine(item.Key + ", " + item.Value);
}
and here is the result
1, A1
2, B
3, C1
4, D1
and if you want to do it inplace you can modify DictA
foreach (var key in DictA.Keys.ToList())
{
DictA[key] = DictB.ContainsKey(DictA[key]) ? DictB[DictA[key]] : DictA[key];
}

Very simple:
var result =
DictA
.ToDictionary(
a => a.Key,
a => DictB.ContainsKey(a.Value)
? DictB[a.Value]
: a.Value);
I get this result:

Since you want to do it inplace
DictA.ToList().ForEach(kv =>
{
if (DictB.ContainsKey(kv.Value))
DictA[kv.Key] = DictB[kv.Value];
});

Here's one way to do what you want:
public static Dictionary<TKey,TValue> Extend<TKey,TValue>( params Dictionary<TKey,TValue>[] sources )
{
return Extend<TKey,TValue>( null , sources ) ;
}
public static Dictionary<TKey,TValue> Extend<TKey,TValue>( IEqualityComparer<TKey> comparer , params Dictionary<TKey,TValue>[] sources )
{
var result = new Dictionary<TKey,TValue>( comparer ?? EqualityComparer<TKey>.Default ) ;
foreach( var src in sources )
{
if ( src == null ) throw new ArgumentNullException("sources", "source dictionary may not be null");
foreach( var item in src )
{
result[item.Key] = item.Value ;
}
}
return result ;
}
Here's another way, using Linq:
public static Dictionary<TKey,TValue> Extend<TKey,TValue>( params Dictionary<TKey,TValue>[] sources )
{
return Extend<TKey,TValue>( null , sources ) ;
}
public static Dictionary<TKey,TValue> Extend<TKey,TValue>( IEqualityComparer<TKey> comparer , params Dictionary<TKey,TValue>[] sources )
{
return sources
.SelectMany( kvp => kvp )
.GroupBy( kvp => kvp.Key , kvp => kvp.Value , comparer )
.ToDictionary( grp => grp.Key , grp => grp.Last() , comparer )
;
}
But I'm willing to bet that the first, straightforward method will be faster.

Related

Efficient way to create a new list based of the differences in values in 2 dictionaries?

I currently have 2 strings that are formatted as an XML that are later converted into dictionaries for comparison.
So, I have a 2 Dictionary<string, object>, dict1 and dict2, that I need to compare. I need to:
Add the key to a list of strings if the values of these two dictionaries do not match
Add the key of dict2 to the list if dict1 does not contain this key
Currently, I have a simple foreach loop
foreach (string propName in dict2.Keys)
{
string oldDictValue;
string newDicValue = dict1[propName].ToString();
if (dict1.ContainsKey(propName))
{
oldDictValue = dict2[propName].ToString();
if (oldDictValue != newDicValue)
list.Add(propName);
}
else
{
list.Add(propName);
}
}
I would like to a faster solution to this problem if possible?
I don't claim that this is any faster, but it should be on par and it's less code:
List<string> list =
dict2
.Keys
.Where(k => !(dict1.ContainsKey(k) && dict1[k].Equals(dict2[k])))
.ToList();
I did do some testing with this:
List<string> list =
dict2
.Keys
.AsParallel()
.Where(k => !(dict1.ContainsKey(k) && dict1[k].Equals(dict2[k])))
.ToList();
That produced a significantly faster run.
Here's how I produced my test data:
var dict1 = Enumerable.Range(0, 10000000).Select(x => Random.Shared.Next(2000000)).Distinct().ToDictionary(x => x.ToString(), x => (object)Random.Shared.Next(20));
var dict2 = Enumerable.Range(0, 10000000).Select(x => Random.Shared.Next(2000000)).Distinct().ToDictionary(x => x.ToString(), x => (object)Random.Shared.Next(20));
You could make it faster by avoiding to get separately the dict1[propName] and the dict2[propName]. You could get the value along with the key, either by enumerating directly the KeyValuePairs stored in the dictionary, or by calling the TryGetValue method:
foreach (var (key, value2) in dict2)
{
if (!dict1.TryGetValue(key, out var value1)
|| value1.ToString() != value2.ToString())
{
list.Add(key);
}
}

Convert ForEach Dictionary Value If Condition And Add Key to List

May I know how can I convert the following statement to LINQ?
Dictionary<Guid, Guid> BatchID_To_RunID = new Dictionary<Guid, Guid>();
List<Guid> BatchIDList_InCase = new List<Guid>();
foreach (var x in BatchID_To_RunID)
{
if (x.Value == RunID)
{
Guid BatchID = x.Key;
BatchIDList_InCase.Add(x.Key);
}
}
Thank you
Well, the if is effectively a filter, suggesting a Where call, and the use of x.Key is effectively a projection, suggesting a Select call. So assuming BatchIDList_InCase is empty before this loop, you could replace it with:
// TODO: Rename BatchID_To_RunID to follow .NET naming conventions
var batchIds = BatchID_To_RunID
.Where(pair => pair.Value == RunID)
.Select(pair => pair.Key)
.ToList();
Or using a query expression:
var batchIds = (from pair in BatchID_To_RunID where pair.Value == RunID select pair.Key)
.ToList();
So you have a dictionary BatchID_To_RunID and a variable RunId
Requirement
Give me the keys of all items in the dictionary that have a Value equal to RunId.
If you write it precisely like this, the solution is not very difficult:
GUID RunId = ...
var result = BatchID_To_RunID // this is a sequence of KeyValuePair<GUID, GUID>
// Keep only those KeyValuePairs with a value equal to RunId
.Where(keyValuePair => keyValuePair.Value == RunId)
// from the remaining keyValuePairs, Select the Key
.Select(keyValuepair => keyValuePair.Key)
// and convert this sequence of GUIDs into a List:
.ToList();
Simple comme bonjour!
This is one way to do it:
BatchIDList_InCase.AddRange(BatchID_To_RunID.Where(b => b.Value == RunID).Select(b => b.Key).ToList());

connect values with the same key in Dictionary<string, string>

I need to combine 2 dictionary. they have the same key (KKK) with different values:
Dictionary<string, string> dic1 = ...Load***();
Dictionary<string, string> dic2 = ...Load***();
dic1:
...
[29] {[RCP, 555501001]}
[30] {[KKK, 04611105012042000120]}
...
dic2:
...
[49] {[SUM, 85737]}
[50] {[KKK, 04611701040040000180]}
...
Union:
Dictionary<string, string> dicUnion= dic1.Union(dic2).OrderBy(k => k.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
result:
ThrowArgumentException: The item with the same key has already been added.
I have connect values with the same key in union Dictionary:
...
[29] {[RCP, "555501001"]}
[30] {[KKK, "04611105012042000120 04611701040040000180"]}
[31] {[SUM, "85737"]}
...
If you really want to use LINQ in this case(i'd prefer a loop):
var dicUnion = dic1.Concat(dic2)
.GroupBy(kv => kv.Key)
.ToDictionary(g => g.Key, g => String.Join(" ", g.Select(kv => kv.Value)));
This will merge both dictionaries but don't care about common keys or different values.
I need to combine 2 dictionary, they have the same key (KKK) with
different values
Ok, if you only want to create a dictionary of common keys:
var union = from kv1 in dic1
join kv2 in dic2
on kv1.Key equals kv2.Key into keyGroup
where keyGroup.Any()
select new KeyValuePair<string, string>(kv1.Key, string.Join(" ", keyGroup.Select(kv => kv.Value)));
Dictionary<string, string> dicUnion = union.ToDictionary(kv => kv.Key, kv => kv.Value);
But instead of concatenating values which have the same key in both dictionaries, i'd use a different approach. Use a Lookup<TKey, TValue>. For example:
var keyLookup = dic1.Concat(dic2).ToLookup(kv => kv.Key, kv => kv.Value);
Now you can do wahtever you want with the values, f.e. create a List<string>:
List<string> values = keyLookup["KKK"].ToList();
I'd use a simple loop to add / append items
Dictionary<string, string> dicUnion = new Dictionary<string, string>(dic1);
foreach(var item in dic2)
{
if(dicUnion.ContainsKey(item.Key))
{
dicUnion[item.Key] += " " + item.Value;
}
else
{
dicUnion.Add(item.Key, item.Value);
}
}
Just try like this;
var intersectedItems = dic1.Where(x => dic2.ContainsKey(x.Key)).Select(x => new
{
Key = x.Key,
Value = x.Value + " " + dic2[x.Key]
}).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
var dicUnion = dic1.Where(x => !intersectedItems.ContainsKey(x.Key))
.Union(dic2.Where(x => !intersectedItems.ContainsKey(x.Key)))
.Union(intersectedItems).OrderBy(k => k.Key)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
If you want to connect ONLY values with the same key, you can use IEnumerable.Join method like this:
var res = dic1.Join(dic2, o1 => o1.Key, o2 => o2.Key, (o1, o2) => new { o1.Key, Value1 = o1.Value, Value2 = o2.Value});
The arguments are the second IEnumerable, key selectors and result selector. In my snippet I create anonymous class but you can create whatever structure you want. The result is again IEnumerable, you can either iterate on or use its ToList(), ToArray(), etc method.
EDIT: after reading comments to other posts I understand you want to get something like this:
dic1.Concat(dic2)
.ToLookup(o => o.Key, o => o.Value)
.ToDictionary(o => o.Key, o => string.Join(" ", o.Distinct()))

LinQ ofType in Value

I have the following Dictionary:
public Dictionary<string,object> Items;
Now I need to get all Items where the Value of the Dictionary-item is from a specific type. (e.g. "int")
var intValues = Items.OfType<KeyValuePair<string,int>> simply does not work.
Code without LinQ would be something like:
var intValues=new Dictionary<string,int>()
foreach (var oldVal in Items) {
if (oldVal.Value is int) {
intValues.add(oldVal.Key, oldVal.Value);
}
}
(Update) my example should show the basic idea. But if possible I would avoid to create a new Dictionary as a result.
The direct translation of your foreach would be the following in LINQ:
var intValues = Items.Where(item => item.Value is int)
.ToDictionary(item => item.Key, item => (int)item.Value);
So basically, you filter first for where the item.Value is an int, and then you create a dictionary from it using ToDictionary where you cast those values to int to make sure that the resulting dictionary is a Dictionary<string, int>. Since we filtered non-integers already, this type cast will always succeed.
You can use the is operator on the Value property:
var intValues = Items.Where(x => x.Value is int);
If you want an actual Dictionary<string,int> at the end just add:
.ToDictionary(v=> v.Key, v=> (int)v.Value)
Try with this:
var intValue = Items
.Where(x => x.Value is int) // filter per Value is int
.ToDictionary(x => x.Key, x => (int)x.Value); // create a new dictionary converting Value to int
You can do
var result = Items.Where(x => x.Value is int)
.ToDictionary(x => x.Key, x => x.Value);

How can I Remove items from dictionary using lambda expression

I am not into LINQ solutions,
I am using simple predicat to determine if the key should be removed,
For example if the dictionary is construct like Dictionary<int, int>,
so how should I remove all the entries with negative data
I am prefer to use the same dictionary, not to create new one,
I don't have preformance issues
Is there a way to do it, without using LINQ, but using Lambda expressions?
I didn't want solutions in LINQ because no one is using them in my project, didn't want to be the first.., but because I saw the LINQ solutions look better, I will use them them..
The simplest way is probably to create a new dictionary, if that's okay for you:
var newDictionary = oldDictionary.Where(pair => pair.Value >= 0)
.ToDictionary(pair => pair.Key,
pair => pair.Value);
If you have to mutate the existing dictionary (e.g. because several other objects have reference to the same dictionary) you'd need to build a list of keys to remove, then remove them afterwards:
var toRemove = dictionary.Where(pair => pair.Value < 0)
.Select(pair => pair.Key)
.ToList();
foreach (var key in toRemove)
{
dictionary.Remove(key);
}
EDIT: I've just noticed the first sentence: "I am not into LINQ solutions". If that means you don't want to use a LINQ solution, here's the by-hand version:
List<int> toRemove = new List<int>();
foreach (KeyValuePair<int, int> pair in dictionary)
{
if (pair.Value < 0)
{
toRemove.Add(pair.Key);
}
}
foreach (var key in toRemove)
{
dictionary.Remove(key);
}
... but if you can use LINQ, I'd encourage you do. My second solution is equivalent to the "by-hand" version, but more readable IMO.
By merely using lambda expression:
foreach (var i in myDict.Where(d => (d.Value < 0 || d.key <0)).ToList() )
{
myDict.Remove(i.Key);
}
var toRemove = dict.Keys.Where(predicate).ToArray();
foreach (var key in toRemove) {
dict.Remove(key);
}
Well if you add
namespace MMExtensions
{
public static class DictionaryExtensions
{
public delegate bool Predicate<TKey, TValue>(KeyValuePair<TKey, TValue> d);
[MethodImpl(MethodImplOptions.Synchronized)]
public static void Filter<TKey, TValue>(
this Dictionary<TKey, TValue> hashtable, Predicate<TKey, TValue> p)
{
foreach (KeyValuePair<TKey, TValue> value in hashtable.ToList().Where(value => !p(value)))
hashtable.Remove(value.Key);
}
}
}
And you had some dataset as dictionary:
Dictionary<string, int> d =
new Dictionary<string, int> {{"v", -3}, {"val1", 1}, {"val2", 2}};
Then you could use:
d.Filter(delegate(KeyValuePair<string, int> kv) { return kv.Value >= 0; });
d.Filter(kv => kv.Value >= 0);// or as lambda
Do you want to remove the items from that dictionary, or are you happy to use a new dictionary without those items included?
var d = new Dictionary<int,int>();
var newDict = d.Where(entry => entry.Value >= 0).ToDictionary(entry => entry.Key, entry => entry.Value);
Easiest one:
Dictionary<long, long> dict...
Dictionary<long, long> result = dict.Were(x => x.Value >= 0).ToDictionary(x => x.Key, x => x.Value);
Or just loop over all in 'for' in reverse order and remove invalid ones.
I know you said you are not into Linq, but I could not contain myself with the following solution, plus it is still useful if you read the title of your question. This is probably the most elegant solution to your problem:
dictionary.Where(pair => pair.Value < 0)
.Select(pair => {
dictionary.Remove(pair.Key);
return pair.Key;
});

Categories