2

(ほとんどの場合) いくつかの値のみが異なる複数の動的オブジェクトがあります。これらのオブジェクトを 1 つのオブジェクトにマージできるようにしたいのですが、競合 (2 つの値が同じではない) が発生した場合は、これらをコレクションまたは別の動的オブジェクト (可能であれば) に格納したいと考えています。

私は expandoObject クラスを使用しているので、オブジェクトをディクショナリにキャストしてマージしようとしますが、競合のあるコレクションを作成するディクショナリのマージに関する記事やソースは見つかりませんでした。

実装が簡単で効率的な方法でこれを行う方法はありますか?

私が達成しようとしていることの簡単なアイデアを提供するために、いくつかのコード サンプルを投稿します。

//returns a dynamic object from JSON (output from DataBase)
JsonReader reader = new JsonReader();
dynamic object = reader.Read(input);

//Creates a dictionairy with all the fieldnames and values.
IDictionary<string, dynamic> properties = (IDictionary<string, dynamic>)object;

//My goal is to merge multiple of these objects into one single object and
//creating collections when values are different from each other.
4

3 に答える 3

3

あなたの問題は IDictionary<> インターフェースに基づいているようです。各キーの値は 1 つだけであると想定しています。IEnumerable<IGrouping<TKey,TValue>>キー付きの値のコレクションのリストを表すLINQ 風に移行することをお勧めします。

.GroupBy または .ToLookup 呼び出しを実行すると、LINQ はそのようなオブジェクトを発行します。
それではプレイしましょう:

using System.Linq;

Dictionary<string, dynamic> A = ...;
Dictionary<string, dynamic> B = ...;

// naiive atempt:
var lookup = 
    A.Keys.Concat(B.Keys)
        .ToDictionary(key => key, key => new dynamic[]{ A[key], B[key] } ));

もちろんうまくいきませんが、どのような問題が発生するかを確認するために書きました。まず、Concat'ing の際に重複したキーを取得する可能性があります。次に、すべてのキーが A と B にあるわけではないため、インデクサーはスローします。次に、たまたま、元のオブジェクトは文字列から 1 つの値への変換であり、既に衝突したオブジェクトで作業する可能性が高いと思いました。次に、これは2つのA / Bオブジェクトのみを使用しますが、複数で作業したい場合があります..

IEnumerable<Dictionary<string, dynamic>> inputs = ....;

// btw. GropuBy returns a lookup, too :) a key -> all matches
var matched_by_keys = inputs.GroupBy(pair => pair.Key);

var final = matched_by_keys.ToDictionary(group => group.Key, group => group);

あっち見て。私はすべての辞書を取得し、それらのキーに従って「それらをペアにしました」。その結果、既存の各キーを、そのキーに以前に割り当てられた一連の値にバインドするルックアップが得られました。結果matched_by_keysは辞書ではなく Enumerable であるため、後で翻訳されました。ToDictionary のパラメーターを見てください。グループ自体は IEnumerable であり、キーを指定する必要があり、グループは変更されていません。

それでも、入力は単一値の IDictionary でのみ機能します。多値入力でもそのようなことを行う必要がある場合は、IDictionaries を検索に簡単に変換して、それらに対して操作を実行できます。

IEnumerable<Dictionary<string, dynamic>> inputs1 = ....; // normal items
IEnumerable<ILookup<string, dynamic>> inputs2 = ....; // items that previously already collided

var allAsLookups =
    inputs1.ToLookup(pair => pair.Key, pair => pair.Value)
    .Concat( inputs2 );

// btw. GropuBy returns a lookup, too :) a key -> all matches
var matched_by_keys = lookups.GroupBy(lk => lk.Key);

var final1 = matched_by_keys.ToDictionary(group => group.Key, group => group.SelectMany(s=>s));

最終的なグループをどのように翻訳する必要があるかに注意してください。この例では、グループはではありませんIEnumerable<Value>が、値が 1 つしかない場合でもIEnumerable<IEnumerable<Value>>、入力が多値になることが許可されているためです。したがって、これは平坦化する必要があり、これは SelectMany によって行われます。アイテムはすでに IEnumerable であったため、as=>s で十分だったため、何も投影する必要はありませんでした。

GroupBy、ToLookup、および ToDictionary のさまざまなオーバーロードを使用すると、多くの有用な効果を実現できます。オーバーロードで遊ぼう!

于 2012-08-15T12:32:28.720 に答える
1

動的オブジェクトがExpandoの場合、次のように Linq を使用できます。

var objectList = new List<dynamic>() {...};

var result = objectList.SelectMany(obj => 
                           (IDictionary<string, dynamic>)obj.ToList())
            .GroupBy(pair => pair.Key)
            .ToDictionary(group => group.Key,
                          group => group.Select(pair => pair.Value));

最初に、動的オブジェクトからSelectManyすべてを選択するために使用しますKeyValuePair<string, dynamic>。この方法では、リストが同じキーを持つことを受け入れます。

次に、キーでグループ化し、関数ToDictionaryを使用して結果を取得します。

于 2012-08-15T15:10:51.700 に答える