1

読み込まれた 2 つの辞書を比較しようとしています。各個人には 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、同じキーを辞書に 2 回追加することになります。検討

マップ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ここで必要なのは、2 つのマップ間で類似しているオブジェクトのセットを単純に知ることのようです。その場合は、値を保存し、 a を使用しSet<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

ラムダでそれを行う方法は次のとおりです。

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

ここで一歩戻りましょう。個人を整数にキー設定する 2 つの辞書があります。ただし、相対辞書のキーを比較するのではなく、それらの値を比較しています。これは、キーが一意ではないと私に思わせます。

あなたが望むのは、両方の辞書の完全な外部結合であるように思えます:

  • 個体が 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 に答える