2015-11-04 16 views
5

MyClassの2つのリストを1つにマージするエレガントで効率的な方法があるかどうか質問したいと思います。2つのリストをプロパティに基づいて1つに結合する

  • ID:int
  • 名:string
  • ExtID:int?

とリストは、リスト内の別のソースとオブジェクトから移入されているが何シェア

MyClassのは、このようになりますIDのように見えます:

私は基本的に必要なものLIST2

ID = someInt (same as List1) 
Name = someString (same as List1) 
ExtID = someInt 

から

そして、MyClassのインスタンスは、これら二つのリストを組み合わせることで、その結果は含むリストです:

ID = someInt (from List1) 
Name = someString (from List1) 
ExtID = someInt (null if no corresponding item - based on ID - on List2) 

私は単純に使用してこれを行うことができます知っていますforeachループですが、よりエレガントで多分(パフォーマンス、可読性のために)好ましい方法があるかどうかを知りたいですか?

+0

同じ 'ID'を持つ2つのインスタンスを1つに結合する' Merge'関数を書くと、リストを連結し、idでグループ化し、最後にマージ関数を使ってグループを折り畳み/縮小することができます。読書のためのlity - パフォーマンスのために、ソートとループでうまくいくでしょう。 – Carsten

答えて

1

優先度が何であるかによって、多くのアプローチがあります。例:連合+検索:

//this will create a key value pairs: id -> matching instances 
var idMap = list1.Union(list2).ToLookup(myClass => myClass.ID); 
//now just select for each ID the instance you want, ex. with some value 
var mergedInstances = idMap.Select(row => 
     row.FirstOrDefault(myClass => myClass.ExtId.HasValue) ?? row.First()); 
上記の利点は、彼らは多くの重複isntancesが含まれている場合でも示しています何のどんな量でも動作しますし、あなたが簡単に

小さな改善合併の条件を変更することができるということです

インスタンスをマージする方法を抽出するために、次のようになります。

MyClass MergeInstances(IEnumerable<MyClass> instances){ 
    return instances.FirstOrDefault(myClass => myClass.ExtId.HasValue) 
      ?? instances.First(); //or whatever else you imagine 
} 

、今ちょうど

var mergedInstances = idMap.Select(MergeInstances); 
上記のコードでそれを使用

清潔で柔軟性があり、シンプルで、追加の条件はありません。パフォーマンスは賢明ではありませんが、誰が気にします。

編集:パフォーマンスを優先するために、いくつかのより多くのオプション

  1. はなく、唯一の小さなリストについては、上記のような検索を行います。次に、より大きなものを反復し、必要な変更を行う。O(m log m)+ O(n)。 m - リストサイズが小さく、リストサイズが大きい - は最も速くなければなりません。

  2. 要素のIDで両方のリストを並べます。 forループを作成すると、両方のリストに対して同じIDを持つ要素への現在のインデックスを保持しながら両方を反復処理します。両方のリストで見つかった次の最小のIDにインデックスを移動します。リストにある場合は、これだけを移動します。O(n log n)+ O(m log m)+ O(n)である。

    私はあなたが、可能性があり、そのクラスのメソッドでは、foreachループを作成するので、毎回あなたが

    instanceList1.MergeLists(instanceList2) 
    

    と、この方法でのようなものを使用したい、そのようなことを行うために必要なsugest思い

+0

List2のforeachループよりもList1の対応する項目に速く動作し、値を設定しますか? ;>そうでなければ、それはライブラリのプライベートメソッドの中に隠されているので、リストをどのくらい大きくすることができ、どのくらいの頻度で使うのか分からないので、代わりにforeachループを使ってパフォーマンスを向上させても構いません。私は間違った方法を述べているかもしれませんが、優先順位はパフォーマンスです。 – user1970395

+1

他のリストの要素を検索する必要がある各要素について、O(n^2)の複雑さがある場合、検索はO(log n)* O(n)実際にはO(n log n)対O(n ^)のforeachループです。私の勝利;)。とにかく行くルックアップテーブル(辞書/地図)を使用する方法です。そして柔軟性も得られます。既存のソリューションを改善できる場合は、リストの代わりに最初から辞書を提供することも考えられます。 – mikus

+1

リストの1つにルックアップ(または辞書)を作成し、次にルックアップを使用して2番目のルックアップをforeachして、値をすばやく見つけることができます。 また、両方のリストの順序付けについて考えてみましょう。また、両方のリストO(n log n)+ O(n)を一度だけ通過するスマートループを実行することもできます。 – mikus

-1

マージ操作で必要なものをすべてコントロールします。

+0

@mikus LINQ *は繰り返しを使用します。イテレータなので、 'foreach'に関するコメントは適用されません。一方、OPがその関数を書く方法を尋ねるとき、 '自分の関数を書く'は良い答えではありません。おそらくこれを削除してコメントとして再投稿する必要がありますか? –

+0

その素晴らしいソリューションについては、内部的にループを使用しないソリューションではありません... – mikus

+0

まあ、私は彼が機能をやる方法を知っていると思っていましたが、これはより良い方法です。 (彼は、私は彼がマージを行う必要があるたびにそれをしたくないと思っていたが、foreachを望んでいないと言ったが、私は彼が好きであると言っていた) –

1

あなたは

var joined = from Item1 in list1 
     join Item2 in list2 
     on Item1.Id equals Item2.Id // join on some property 
     select new MyClass(Item1.Id, Item1.Name, Item1.ExtID??Item2.ExtID); 

には、編集したいものをこのです:

..あなたが外を探しているなら参加し、

var query = from Item1 in list1 
      join Item2 in list2 on Item1.Id equals Item2.Id into gj 
      from sublist2 in gj.DefaultIfEmpty() 
      select new MyClass(Item1.Id, Item1.Name, sublist2??string.empty); 

可読性が賢明、foreachループを使用すると、あまりにも悪い考えではありません

+0

それはいくつかのIDがあれば、リストの1つに欠けている – mikus

+0

ええ。 – Godsent

関連する問題