1

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

到目前为止的代码是

            _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

4 回答 4

1

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

地图1

  • 关键1,价值狗

地图2

  • 关键 2,价值狗
  • 关键 3,价值狗

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

// 1:Dog matches 2:Dog
similarInds.Add(1, "Dog");
// 1:Dog matches 3:Dog
similarInds.Add(1, "Dog");

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

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

similarInds.Add(kvpInd.Value);
于 2012-04-04T15:17:38.910 回答
1

字典中可以有多个具有相同值的条目。您没有做任何事情来检查或比较密钥。

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

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

或者你可以使用

_Individuals1.Values.Intersect(_Individuals2.Values);

此外,在使用泛型时,几乎总是可以覆盖存储在泛型中的对象的相等运算符。然后,您不必执行以下操作:

if (kvpInd.Value.name.name == kvpInd2.Value.name.name)
于 2012-04-04T15:18:57.560 回答
1

以下是使用 lambda 的方法:

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);
于 2012-04-04T15:24:14.253 回答
0

让我们退后一步。您有两个字典,它们将 Individuals 键入一个整数。但是,您不是在比较相对字典的键,而是比较它们的值。这让我认为键不是唯一的。

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

  • 如果个人仅存在于 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);

您可以避免显式创建“左”可枚举,通过构造产生“连接”的查询以产生左连接而不是我展示的内连接。您还可以尝试使用 _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 的一些(但可能不是全部)密钥。这是否“更好”取决于具体情况。

于 2012-04-04T15:42:58.830 回答