2017-11-15 7 views
1

私はオブジェクトのコレクションを持っていますIEnumerable<object> obs。 私はオブジェクトの別のコレクションIEnumerable<object> dataを持っています。私はobなどの特定のプロパティに同じ値を有するdataの最初の項目を見つける必要がobsobについてあちこちに区切りのある列挙型を構築する

。例えば、私はdataの最初の項目がToString()の値がobと同じものを探している可能性があります。プロパティ値が一致する最初の項目が見つかると、見つかったデータ項目で何かを実行し、次にobobsにチェックします。何も見つからなければ、私はエラーを投げます。

foreach (object ob in obs) 
{ 
    foreach (object dataOb in data) 
     if (ob.ToString() == dataOb.ToString()) 
     { 
      ... // do something with dataOb 
      goto ContinueOuter; 
     } 
    throw new Exception("No matching data found."); 

    ContinueOuter: ; 
} 

欠点は、私は不必要であるたびに、dataOb.ToString()計算することである。ここでは

は単純なアプローチです。 私はそれをキャッシュすることができます:

IDictionary<object, string> dataToDataStr = new Dictionary<object, string>(); 
foreach (object dataObj in data) // collect all ToString values in advance 
    dataToDataStr.Add(dataObj, dataObj.ToString()); 

foreach (object ob in obs) 
{ 
    foreach (object dataOb in dataToDataStr.Keys) 
     if (ob.ToString() == dataToDataStr[dataOb]) 
     { 
      ... // do something with dataOb 
      goto ContinueOuter; 
     } 
    throw new Exception("No matching data found."); 

    ContinueOuter: ; 
} 

欠点は、それが必要ではないかもしれないにもかかわらず、私はすべてのToString()値を計算することです。私は、データ収集の前半にすべての一致するデータオブジェクトを見つけるかもしれません。

どのようにしてdataToDataStr辞書(またはオブジェクトと唯一計算済みのToString値の両方を取得できる他の列挙可能なデータ構造)を遅延して構築できますか?ここで

は、私は心の中で持っているもののコード(擬似コードと混合)である:

IDictionary<object, string> dataToDataStr = new Dictionary<object, string>(); 
object lastProcessedDataOb = null; 

foreach (object ob in obs) 
{ 
    foreach (object dataOb in dataToDataStr.Keys) 
     if (ob.ToString() == dataToDataStr[dataOb]) 
     { 
      ... // do something with dataOb 
      goto ContinueOuter; 
     } 

    foreach (object dataOb in data STARTING AFTER lastProcessedDataOb) 
    // if lastProcessedDataOb == null, start with the first entry of data 
    { 
     dataToDataStr.Add(dataOb, dataOb.ToString(); 
     lastProcessedDataOb = dataOb; 

     if (ob.ToString() == dataToDataStr[dataOb]) 
     { 
      ... // do something with dataOb 
      goto ContinueOuter; 
     } 
    } 
    throw new Exception("No matching data found."); 

    ContinueOuter: ; 
} 

dataは、インデックス付きのアクセスとLinkedListまたは任意のコレクションだった(その後、私はリンクを保存することができれば、私はそれが簡単である知っていますリストノードまたはインデックスはlastProcessedDataOb)、そうではありません - IEnumerableです。多分yield returnをここで使用できますか?

+0

...このような複雑さの価値である、「ToStringメソッド」は単なる例のためのものであり、あなたはそこにいくつかの複雑な計算を持っていることを望みます場所。それは間違っていると感じます。なぜEquals/GetHashcodeを実装する標準的な方法や、IEquatable/IComparableインターフェイスの適切な実装を使用しないのですか?実装には、計算が高価な場合はキャッシュ機構が含まれている可能性があります。そうすれば、そのようなクラスのユーザーはもっと楽になります。 – Ralf

+0

あなたのコメントは質問のポイントとは関係ありません(列挙可能なlazily)。また、私は次のように書いています。「ある特定のプロパティで同じ値を持つデータの最初の項目をob [...] 'ToString' [...]」として検索する必要があります。 – Kjara

答えて

1

あなたのコレクションは本当に大きく、あなたが本当にない場合

  1. 特定の項目がある
  2. 場合は、すでに計算された項目のキャッシュを作成します:あなたは、次のアプローチを使用することができ、dataの各項目についてToStringを評価したいです私はキャッシュを見つけた - それは素晴らしいです、我々は一致があります。
  3. それ以外の場合は、一致するまでdataコレクションを繰り返してキャッシュにデータを挿入します。これは、foreachの代わりに、**Enumerator**のデータ収集を手動で制御して効率的に行うことができます。

    IEnumerable<object> obs; 
    IEnumerable<object> data; 
    Dictionary<string, object> dataCache = new Dictionary<string, object>(); 
    
    var dataIterator = data.GetEnumerator(); 
    foreach (var ob in obs) 
    { 
        var obText = ob.ToString(); 
        object matchingDataItem = null; 
        if (!dataCache.TryGetValue(obText, out matchingDataItem)) 
        { 
         while (dataIterator.MoveNext()) 
         { 
          var currentData = dataIterator.Current; 
          var currentDataText = currentData.ToString(); 
          if (!dataCache.ContainsKey(currentDataText)) // Handle the case when data collection contains duplicates 
          { 
           dataCache.Add(currentDataText, currentData); 
           if (currentDataText == obText) 
           { 
            matchingDataItem = currentData; 
            break; 
           } 
          } 
         } 
        } 
        if (matchingDataItem != null) 
        { 
         Console.WriteLine("Matching item found for " + obText); 
        } 
        else 
        { 
         throw new Exception("No matching data found."); 
        } 
    } 
    

すべてobsアイテムが発見され、あなたは、各項目の詳細、その後一度ためToStringを評価しませんときに保証は唯一のポイントにdataコレクションを反復処理することができますこの方法です。

PS:私はあなたが最初にToStringメソッドを経由して、あなたのオブジェクトを比較する理由はたぶん、あなたは説明する必要があり

+0

'dataCache'にすでに' currentDataText'キーが含まれているかどうかチェックする必要があります'Add'コール。キーが存在する場合は、「続行」を実行します。 – Kjara

+0

@Kjaraありがとう、私はコレクションに重複があるとは思わなかった... –

+0

キーが重複している場合でも、文字列の等価性( 'currentDataText == obText')をチェックする。すでに 'dataCache.TryGetValue(...) 'を介してチェックされているので、これは必要ではありません。 – Kjara

-1

は完全に(私はC#7の新しい値タプル表記を使用)これは動作するはず ... LINQは、遅延評価を使用していることを忘れてしまった:

IEnumerable<(object, string)> objStrPairs = data.Select(o => (o, o.ToString())); 
foreach (object ob in obs) 
{ 
    foreach ((object, string) dataPair in objStrPairs) 
     if (ob.ToString() == objStrPairs.Item2) 
     { 
      ... // do something with objStrPairs.Item1 
      goto ContinueOuter; 
     } 
    throw new Exception("No matching data found."); 

    ContinueOuter: ; 
} 
+0

なぜあなたは 'goto'を使っていますか?これは互換性の理由でのみ推奨されていません。 –

+0

@CamiloTerevinto Javaではループに名前を付けることができます。外側のループは「外側」と名付けられ、次にネストされたループの内側では、「外側を続ける」と呼ばれます。 C#にはこの機能がないので、 'goto'が道です。 – Kjara

+0

一般的な方法は、C#がjavaではないことを理解することです。通常、内部ループで何が起こったのかを知るために、外部ループにブール変数を追加します。また、この答え全体に対して、単一のLINQ文を使用することもできます。 –

関連する問題