比较两个词典并将字典值添加到另一个词典C#

时间:2012-04-04 15:07:42

标签: c# dictionary comparison

我正在尝试比较加载的两个词典。每个人都包含一个ID和一个Individual对象。

到目前为止的代码是

            _Individuals1 = file1.fileIndividuals;
        _Individuals2 = file2.fileIndividuals;

        foreach (KeyValuePair<int, Individual> kvpInd in _Individuals1)
        {
            foreach (KeyValuePair<int, Individual> kvpInd2 in _Individuals2)
            {
                if (kvpInd.Value.name.name == kvpInd2.Value.name.name)
                {
                    similarInds.Add(kvpInd.Key, kvpInd.Value);
                }
            }
        }

我得到的错误是“已经添加了具有相同键的项目”。 我可以看到为什么,但我不知道如何以不同的方式去做它的工作。有人可以帮忙吗?

谢谢

4 个答案:

答案 0 :(得分:1)

这里的问题是你对不同Dictionary<TKey, TValue>个实例中的键和值之间的关系有一个错误的假设。多个条目很可能具有不同的键但具有相同的值。如果在_Individuals2中发生这种情况,那么您最终会将相同的密钥添加到字典中两次。考虑

地图1

  • Key 1,Value Dog

地图2

  • Key 2,Value Dog
  • Key 3,Value Dog

在这种情况下,Map2中的多个值具有值Dog。所以我最终基本上根据你的算法

执行以下操作
// 1:Dog matches 2:Dog
similarInds.Add(1, "Dog");
// 1:Dog matches 3:Dog
similarInds.Add(1, "Dog");

看起来你想要的只是简单地知道两个地图之间相似的Individual个对象集。如果是,则只需存储该值并使用Set<Individual>以防止重复。

var similarInds = new HashSet<Individual>();
...

similarInds.Add(kvpInd.Value);

答案 1 :(得分:1)

您可以在字典中使用相同值的多个条目。您无需检查或比较密钥。

因此,_Individuals2中有多个条目具有相同的值,但它们具有不同的键。

我不知道你在这里做了什么,但我认为你的关键应该是每个对象的独特之处,他们真的不应该比较这些值。如果您使用列表或类似的东西,您可以使用交叉点方法返回它们的共同点。

或者你可以使用

_Individuals1.Values.Intersect(_Individuals2.Values);

此外,在使用泛型时,几乎总是能够覆盖存储在泛型中的对象的相等运算符。那你就不必做以下事情:

if (kvpInd.Value.name.name == kvpInd2.Value.name.name)

答案 2 :(得分:1)

以下是使用lambdas的方法:

var similarInds = file1.fileIndividuals.
Where(kv1 => file2.fileIndividuals.Any(kv2 => kv1.Value.name.name == kv2.Value.name.name)).
ToDictionary(kv => kv.Key, kv => kv.Value);

答案 3 :(得分:0)

我们在这里退后一步。你有两个词典,将个人键入一个整数。但是,您不是要比较相对词典的键,而是比较它们的值。这让我觉得钥匙不是唯一的。

听起来你想要的是两个词典的完全外连接:

  • 如果个人仅存在于File1中,请使用来自File1的个人的密钥和值。
  • 仅适用于File2中的个人。
  • 如果两个文件包含相同的个人(按名称),请合并记录。

这可以通过一些Linq来完成。理解这不是最可能的解决方案,但是更容易理解正在发生的事情。

//get records from 1 that aren't in 2
var left = _Individuals1.Where(l=>!_Individuals2.Any(r=>l.Value.Name == r.Value.Name));
//get records that appear in both 1 and 2, 
//using the select clause to "merge" the data you want from each side
var join = from l in _Individuals1
           join r in _Individuals2 on l.Value.Name equals r.Value.Name
           select new KeyValuePair<int, Individual>(l.Key, r.Value);
//get records from 2 that aren't in 1
var right = _Individuals2.Where(r=>!_Individuals1.Any(l=>l.Value.Name == r.Value.Name));

//Now, the keys from the left and join enumerables should be consistent,
//because we used the keys from _Individuals1 in both of them.
var merged = left.Concat(join).ToDictionary(x=>x.Key, x=>x.Value);

//BUT, keys from records that only existed in 2 may have duplicate keys, 
//so don't trust them
var maxKey = merged.Keys.Max();
foreach(var r in right)
   merged.Add(++maxKey, r.Value);

通过构造生成“join”的查询来生成左连接而不是我显示的内连接,可以避免显式创建“left”可枚举。您还可以尝试使用_Individuals2中的键,通过针对合并的目录检查每个键。该代码看起来像这样:

var maxKey = merged.Keys.Max();
foreach(var r in right)
   if(merged.ContainsKey(r.Key))
      merged.Add(++maxKey, r.Value);
   else
   {
      merged.Add(r.Key, r.Value);
      maxKey = r.Key > maxKey ? r.Key : maxKey;
   }

只要密钥不重复,这将安全地使用来自_Individuals2的密钥,因此应该使用来自_Individuals2的一些(但可能不是全部)密钥。这是否“更好”取决于具体情况。