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

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

Related

compare two dictionaries by key to obtain values of matching keys

I have two dictionaries A and B.
A - (a,b) (c,d) (e,f)
B - (a,p) (c,q) (g,h)
I want to be able to make a new dictionary C which will be as below -
C - (b,p) (d,q)
Is there any way I can do this?
This is what I currently have:
var C= B.Where(d => A.ContainsKey(d.Key)).ToList();
Easy with Linq ;)
var query =
from x in dictionary1
join y in dictionary2 on x.Key equals y.Key
select new { Value1 = x.Value, Value2 = y.Value };
var newDict = query.ToDictionary(item => item.Value1, item => item.Value2);
However it's not the most efficient approach, since it doesn't take advantage of the dictionary's fast lookup. A faster approach would be something like this:
var newDict = new Dictionary<string, string>(); // adjust the key and value types as needed
foreach (var kvp in dictionary1)
{
string value2;
if (dictionary2.TryGetValue(kvp.Key, out value2))
{
newDict.Add(kvp.Value, value2);
}
}

Using LINQ to build a Dictionary from a List of delimited strings

I have a list of strings that look like this:
abc|key1|486997
def|key1|488979
ghi|key2|998788
gkl|key2|998778
olz|key1|045669
How can I use LINQ and ToDictionary to produce a Dictionary<string, List<string>> that looks like
key1 : { abc|key1|486997, def|key1|488979, olz|key1|045669 }
key2 : { ghi|key2|998788, gkl|key2|998778 }
Basically I want to be able to extract the second element as the key use ToDictionary() to create the dictionary in one go-round.
I'm currently doing this ..
var d = new Dictionary<string, List<string>>();
foreach(var l in values)
{
var b = l.Split('|');
var k = b.ElementAtOrDefault(1);
if (!d.ContainsKey(k))
d.Add(k, new List<string>());
d[k].Add(l);
}
I've seen the questions on building dictionaries from a single string of delimited values, but I'm
wondering if there's an elegant way to do this when starting with a list of delimited strings instead.
var list = new []
{
"abc|key1|486997",
"def|key1|488979",
"ghi|key2|998788",
"gkl|key2|998778",
"olz|key1|045669"
};
var dict = list.GroupBy(x => x.Split('|')[1])
.ToDictionary(x => x.Key, x => x.ToList());
You can also transform it to a lookup (that is very similary to a Dictionary<K,IEnumerable<V>>) in one shot:
var lookup = list.ToLookup(x => x.Split('|')[1]);
var data = new[]
{
"abc|key1|486997",
"def|key1|488979",
"ghi|key2|998788",
"gkl|key2|998778",
"olz|key1|045669"
};
var dictionary = data.Select(row => row.Split('|'))
.GroupBy(row => row[1])
.ToDictionary(group => group.Key, group => group);
If your data is guaranteed to be consistent like that, you could do something like this:
var data = new[]
{
"abc|key1|486997",
"def|key1|488979",
"ghi|key2|998788",
"gkl|key2|998778",
"olz|key1|045669"
};
var items = data
.GroupBy(k => k.Split('|')[1])
.ToDictionary(k => k.Key, v => v.ToList());

Extracting a Dictionary<TKey, double> from a Dictionary<TKey, string> instance

I have a generic Dictionary where TValue is of type String (Dictionary<int, string>). I chose to use string as the value type because the data was loaded from an Xml file where the source values can be character or numeric data types (I suppose Object would've been an acceptable TValue type too, but even then this question would be equally applicable).
The character data types also have importance, so excluding them outright isn't an option.
I'd like to extract a subset of this Dictionary<int, double>. In other words, I'd like the subset of the dictionary where the values are numeric.
Right now I'm doing it like this:
Dictionary<int, string> myDictionary;
// Do some loading.
var numericData = myDictionary.Where(kvp => Double.TryParse(kvp.Value, out temp)
This approach is awfully ugly and doesn't get me the result as a Dictionary<int, double> Can anyone offer other ways to improve this?
Thanks!
The code you've given is not only ugly - it will fail with an InvalidCastException at execution time. I suspect you actually want:
var numericData = myDictionary
.Select(kvp => {
double value;
return new { kvp.Key,
Value = double.TryParse(kvp.Value, out value)
? value : (double?) null
};
})
.Where(pair => pair.Value != null)
.ToDictionary(pair => pair.Key, pair => pair.Value.Value);
And yes, that's ugly - but:
It avoids parsing the value more than once
It avoids putting side-effects in your query
You can make it slightly cleaner but less efficient if you're happy to parse twice:
var numericData = myDictionary
.Where(kvp => { double tmp; return double.TryParse(kvp.Value, out tmp); })
.ToDictionary(pair => pair.Key, pair => double.Parse(pair.Value));
Or (more cleanly) you could create a separate method:
public static double? TryParseNullableDouble(string text)
{
double value;
return double.TryParse(text, out value) ? value : (double?) null;
}
Then the first version becomes:
var numericData = myDictionary
.Select(kvp => new { kvp.Key, TryParseNullableDouble(kvp.Value) })
.Where(pair => pair.Value != null)
.ToDictionary(pair => pair.Key, pair => pair.Value.Value);
You can just create a new dictionary using the temp variable which contains the double value - this exploits the fact that the enumeration and addition to the dictionary is done item by item so temp contains the correct double value:
double temp = 0;
var numDict = myDictionary.Where(kvp => Double.TryParse(kvp.Value, out temp))
.ToDictionary( x=> x.Key, x=> temp);

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

Get keys as List<> from Dictionary for certain values

While similar to this question which gave me the LINQ for part of my problem, I'm missing something that seems like it must be obvious to avoid the last step of looping through the dictionary.
I have a Dictionary and I want to get a List of keys for just the items for which the value is true. Right now I'm doing this:
Dictionary<long,bool> ItemChecklist;
...
var selectedValues = ItemChecklist.Where(item => item.Value).ToList();
List<long> values = new List<long>();
foreach (KeyValuePair<long,bool> kvp in selectedValues) {
values.Add(kvp.Key);
}
Is there any way I can go directly to a List<long> without doing that loop?
To do it in a single statement:
var values = ItemChecklist.Where(item => item.Value).Select(item => item.Key).ToList();
Try using Enumerable.Select:
List<long> result = ItemChecklist.Where(kvp => kvp.Value)
.Select(kvp => kvp.Key)
.ToList();

Categories