问题
I have two Generic Dictionaries.Both have same keys.But values can be different.I want to compare 2nd dictionary with 1st dictionary .If there are differences between values i want to store those values in separate dictionary.
1st Dictionary
------------
key Value
Barcode 1234566666
Price 20.00
2nd Dictionary
--------------
key Value
Barcode 1234566666
Price 40.00
3rd Dictionary
--------------
key Value
Price 40
Can Anyone give me a best algorithm to do this.I wrote an algorithm but it have lot of loops. I am seeking a short and efficient idea.Also like a solution by using LINQ query expression or LINQ lamda expression. I am using .Net Framework 3.5 with C#. I found some thing about Except() method. But Unfortunately i couldn't understand what is happening on that method. It is great if any one explains the suggested algorithm.
回答1:
If you've already checked that the keys are the same, you can just use:
var dict3 = dict2.Where(entry => dict1[entry.Key] != entry.Value)
.ToDictionary(entry => entry.Key, entry => entry.Value);
To explain, this will:
- Iterate over the key/value pairs in
dict2 - For each entry, look up the value in
dict1and filter out any entries where the two values are the same - Form a dictionary from the remaining entries (i.e. the ones where the
dict1value is different) by taking the key and value from each pair just as they appear indict2.
Note that this avoids relying on the equality of KeyValuePair<TKey, TValue> - it might be okay to rely on that, but personally I find this clearer. (It will also work when you're using a custom equality comparer for the dictionary keys - although you'd need to pass that to ToDictionary, too.)
回答2:
try :
dictionary1.OrderBy(kvp => kvp.Key)
.SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))
回答3:
to check any difference,
dic1.Count == dic2.Count && !dic1.Except(dic2).Any();
following code return all the different values
dic1.Except(dic2)
回答4:
You mentioned that both dictionaries have the same keys, so if this assumption is correct, you don't need anything fancy:
foreach (var key in d1.Keys)
{
if (!d1[key].Equals(d2[key]))
{
d3.Add(key, d2[key]);
}
}
Or am I misunderstanding your problem?
回答5:
Assuming both dictionaries have the same keys, the simplest way is
var result = a.Except(b).ToDictionary(x => x.Key, x => x.Value);
EDIT
Note that a.Except(b) gives a different result from b.Except(a):
a.Except(b): Price 20
b.Except(a): Price 40
回答6:
var diff1 = d1.Except(d2);
var diff2 = d2.Except(d1);
return diff1.Concat(diff2);
Edit: If you sure all keys are same you can do:
var diff = d2.Where(x=>x.Value != d1[x.Key]).ToDictionary(x=>x.Key, x=>x.Value);
回答7:
You should be able to join them on their keys and select both values. Then you can filter based on whether the values are the same or different. Finally, you can convert the collection to a dictionary with the keys and second values.
var compared = first.Join( second, f => f.Key, s => s.Key, (f,s) => new { f.Key, FirstValue = f.Value, SecondValue = s.Value } )
.Where( j => j.FirstValue != j.SecondValue )
.ToDictionary( j => j.Key, j => j.SecondValue );
Using a loop shouldn't be too bad either. I suspect that they would have similar performance characteristics.
var compared = new Dictionary<string,object>();
foreach (var kv in first)
{
object secondValue;
if (second.TryGetValue( kv.Key, out secondValue ))
{
if (!object.Equals( kv.Value, secondValue ))
{
compared.Add( kv.Key, secondValue );
}
}
}
回答8:
converting the object to dictionary then following set concept subtract them, result items should be empty in case they are identically.
public static IDictionary<string, object> ToDictionary(this object source)
{
var fields = source.GetType().GetFields(
BindingFlags.GetField |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source) ?? string.Empty
);
var properties = source.GetType().GetProperties(
BindingFlags.GetField |
BindingFlags.GetProperty |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null) ?? string.Empty
);
return fields.Concat(properties).ToDictionary(key => key.Key, value => value.Value); ;
}
public static bool EqualsByValue(this object source, object destination)
{
var firstDic = source.ToFlattenDictionary();
var secondDic = destination.ToFlattenDictionary();
if (firstDic.Count != secondDic.Count)
return false;
if (firstDic.Keys.Except(secondDic.Keys).Any())
return false;
if (secondDic.Keys.Except(firstDic.Keys).Any())
return false;
return firstDic.All(pair =>
pair.Value.ToString().Equals(secondDic[pair.Key].ToString())
);
}
public static bool IsAnonymousType(this object instance)
{
if (instance == null)
return false;
return instance.GetType().Namespace == null;
}
public static IDictionary<string, object> ToFlattenDictionary(this object source, string parentPropertyKey = null, IDictionary<string, object> parentPropertyValue = null)
{
var propsDic = parentPropertyValue ?? new Dictionary<string, object>();
foreach (var item in source.ToDictionary())
{
var key = string.IsNullOrEmpty(parentPropertyKey) ? item.Key : $"{parentPropertyKey}.{item.Key}";
if (item.Value.IsAnonymousType())
return item.Value.ToFlattenDictionary(key, propsDic);
else
propsDic.Add(key, item.Value);
}
return propsDic;
}
originalObj.EqualsByValue(messageBody); // will compare values.
source of the code
回答9:
In recent C# versions you can try
public static Dictionary<TK, TV> ValueDiff<TK, TV>(this Dictionary<TK, TV> dictionary,
Dictionary<TK, TV> otherDictionary)
{
IEnumerable<(TK key, TV otherValue)> DiffKey(KeyValuePair<TK, TV> kv)
{
var otherValue = otherDictionary[kv.Key];
if (!Equals(kv.Value, otherValue))
{
yield return (kv.Key, otherValue);
}
}
return dictionary.SelectMany(DiffKey)
.ToDictionary(t => t.key, t => t.otherValue, dictionary.Comparer);
}
I am not sure that SelectManyis always the fastest solution, but it is one way to only select the relevant items and generate the resulting entries in the same step. Sadly C# does not support yield return in lambdas and while I could have constructed single or no item collections, I choose to use an inner function.
Oh and as you say that the keys are the same, it may be possible to order them. Then you could use Zip
public static Dictionary<TK, TV> ValueDiff<TK, TV>(this Dictionary<TK, TV> dictionary,
Dictionary<TK, TV> otherDictionary)
{
return dictionary.OrderBy(kv => kv.Key)
.Zip(otherDictionary.OrderBy(kv => kv.Key))
.Where(p => !Equals(p.First.Value, p.Second.Value))
.ToDictionary(p => p.Second.Key, p => p.Second.Value, dictionary.Comparer);
}
Personally I would tend not to use Linq, but a simple foreach like carlosfigueira and vanfosson:
public static Dictionary<TK, TV> ValueDiff2<TK, TV>(this Dictionary<TK, TV> dictionary,
Dictionary<TK, TV> otherDictionary)
{
var result = new Dictionary<TK, TV>(dictionary.Count, dictionary.Comparer);
foreach (var (key, value) in dictionary)
{
var otherValue = otherDictionary[key];
if (!Equals(value, otherValue))
{
result.Add(key, otherValue);
}
}
return result;
}
来源:https://stackoverflow.com/questions/9547351/how-to-compare-two-dictionaries-in-c-sharp