2012-03-03 82 views
11

我有两个通用词典。两者都有相同的键。但值可以不同。我想比较第二个词典与第一个词典。如果两个值之间存在差异,我想将这些值存储在单独的字典。如何比较C#中的两个词典

1st Dictionary 
------------ 
key  Value 

Barcode 1234566666 
Price  20.00 


2nd Dictionary 
-------------- 
key  Value 

Barcode 1234566666 
Price  40.00 


3rd Dictionary 
-------------- 
key  Value 

Price  40 

任何一个可以给我一个最好的算法来干这事写一个算法,但它有很多loops.I的正在谋求高效短idea.Also等的溶液使用LINQ查询表达式或LINQ lamda expression.I使用.Net Framework 3.5与C#。我发现了一些关于Except()方法的东西。但不幸的是,我不明白在该方法上发生了什么。如果任何人解释了建议的算法,这是非常好的。我总是喜欢学习:)。

谢谢 Thabo。

+0

你要什么做的,如果一个关键出现在第一个字典而不是第二,反之亦然? – 2012-03-03 15:45:58

+0

不......实际的密钥在名称和计数上必须相同。我在使用iscontains()方法检查算法之前进行检查。预先感谢。 – Thabo 2012-03-03 15:52:08

回答

24

如果您已经检查了密钥是相同的,你可以使用:

var dict3 = dict2.Where(entry => dict1[entry.Key] != entry.Value) 
       .ToDictionary(entry => entry.Key, entry => entry.Value); 

讲解,这将:

  • 遍历的键/值对dict2
  • 对于每个条目,查找dict1中的值并过滤掉两个值相同的任何条目
  • 形成词典y从剩余的条目(即dict1值不同的那些)通过从每一对中获取密钥和值,就像它们出现在dict2中那样。

注意,这避免了依靠KeyValuePair<TKey, TValue>平等 - 这也许还好依靠这一点,但我个人认为这更清晰。 (它也将在您使用自定义相等比较的字典键的作用 - 虽然你需要传递到ToDictionary,太)

6

你提到的两个字典具有相同的密钥,因此,如果这个假设是正确的,你不需要任何幻想:

 foreach (var key in d1.Keys) 
     { 
      if (!d1[key].Equals(d2[key])) 
      { 
       d3.Add(key, d2[key]); 
      } 
     } 

还是我误解你的问题?

+0

你应该做'!d1 [key] .Equals(d2 [key])' – 2012-03-03 15:59:59

+0

ooops ...这很简单..我可以这样想,但有一点怀疑是这样的表现。因为我总是喜欢避免Order n操作。 – Thabo 2012-03-03 16:00:53

+1

你错过了一个'!',因为你想要不同的价值观,而不是平等的价值观。 – 2012-03-03 16:01:05

2

你应该可以在他们的键上加入他们并选择两个值。然后,您可以根据值是相同还是不同来过滤。最后,您可以使用键和第二个值将集合转换为字典。

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

使用循环也不应该太糟糕。我怀疑他们会有类似的表现特征。

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

尝试:

dictionary1.OrderBy(kvp => kvp.Key) 
      .SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key)) 
+1

这是比较值?这是返回一个集合或布尔值。 – Thabo 2012-03-03 16:04:17

+0

我尝试了几个不同的选项,字典键是一个字符串,这个似乎是迄今为止最快的。 – 2017-04-03 20:44:15

2

假设两个字典按键相同,最简单的方法是

var result = a.Except(b).ToDictionary(x => x.Key, x => x.Value); 

编辑

注意a.Except(b)给出了不同的结果b.Except(a)

a.Except(b): Price  20 
b.Except(a): Price  40 
+0

如果这两个字典中的密钥顺序不同,该怎么办? – Thabo 2012-03-03 15:55:30

+1

@Thabo是的。但请注意'a.Except(b)'与'b.Except(a)'有不同的结果。 – 2012-03-03 15:56:47

2
var diff1 = d1.Except(d2); 
var diff2 = d2.Except(d1); 
return diff1.Concat(diff2); 

编辑:如果你确保所有的按键都一样,你可以这样做:

var diff = d2.Where(x=>x.Value != d1[x.Key]).ToDictionary(x=>x.Key, x=>x.Value); 
+0

虽然这不会导致字典,但带有'IEnumerable >' – 2012-03-03 16:04:01

+1

首先,我们不能有字典,但对于第二个,我会更新我的答案。 – 2012-03-03 16:18:13

5

检查任何区别,

dic1.Count == dic2.Count && !dic1.Except(dic2).Any(); 

下面的代码返回所有的不同的值

dic1.Except(dic2) 
+0

你能解释这一点吗?:) – Thabo 2012-03-03 16:14:18

+1

@Thabo:如果字典大小相同并且没有第一个元素,也不在第二个字符,它们是等价的。第二行直接返回所有在第一个而不在第二个的元素 – 2012-03-03 16:18:39

0

转换的对象字典,然后下面的一组概念相减如果结果相同,结果项目应该是空的。

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