5

入力された一連の値を要約する関数を Linq を使用して作成したいと思います。関数は次のようになります。

IDictionary<TKey, Summary<TKey>> Summarize<TKey, TValue>(IEnumerable<TValue> values)
{
    return values
        .ToLookup(val => GetKey(val))         // group values by key
        .Union(*an empty grouping*)           // make sure there is a default group
        .ToDictionary(
            group => group.Key,
            group => CreateSummary(group));   // summarize each group
}

問題は、着信シーケンスにそのキーの値が含まれていない場合でも、結果の IDictionary に default(TKey) のエントリが必要であるということです。これは純粋に機能的な方法で行うことができますか? (変更可能なデータ構造を使用していません。)

私が考えることができる唯一の方法は、辞書にパイプする前にルックアップで .Union を呼び出すことです。しかし、それには空の IGrouping を作成する必要があり、これは明示的なクラスなしでは可能ではないようです。これを行うエレガントな方法はありますか?

編集: TKey は値型であると想定できます。

4

3 に答える 3

5

受け入れられた答えは私が探していたものですが、うまくいきませんでした。多分私は何かを逃しましたが、コンパイルされませんでした。それを修正するには、コードを変更する必要がありました。これが私のために働いたコードです:

public class EmptyGroup<TKey, TValue> : IGrouping<TKey, TValue>
{
    public TKey Key { get; set; }

    public IEnumerator<TValue> GetEnumerator()
    {
        return Enumerable.Empty<TValue>().GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

このように使用

var emptyGroup = new EmptyGroup<Customer, AccountingPaymentClient>();
于 2014-01-15T17:33:47.363 に答える
4

GroupBy や ToLookup から空のグループを取得することはできません。意図的な理由があるのか​​もしれません。

これは純粋に機能的な方法で行うことができますか? (変更可能なデータ構造を使用していません。)

このようなアカデミックな要件は楽しいこともありますが、どのソリューションも単純な実装の単純さと比較する必要があります。

Dictionary<TKey, Summary<TKey>> result = values
  .GroupBy(val => GetKey(val))
  .ToDictionary(g => g.Key, g => CreateSummary(g));

TKey x = default(TKey);
if (!result.ContainsKey(x))
{
  result[x] = CreateSummary(Enumerable.Empty<TValue>());
}

return result;

空のグループが必要な場合は、そのためのクラスを追加するだけです:

public class EmptyGroup<TKey, TValue> : IGrouping<TKey, TValue>
{
  public TKey Key {get;set;}

  public IEnumerator GetEnumerator()
  {
    return GetEnumerator<TValue>();
  }
  public IEnumerator<TValue> GetEnumerator<TValue>()
  {
    return Enumerable.Empty<TValue>().GetEnumerator<TValue>();
  }
}

次のように使用します。

EmptyGroup<TKey, TValue> empty = new EmptyGroup<TKey, TValue>(Key = default<TKey>());
于 2011-11-08T16:07:19.030 に答える
2

ルックアップ テーブルにエントリがあることを確認し、ない場合は新しいルックアップ テーブルを作成する 2 番目の選択を追加できます。これは、他の値がある場合にデフォルト値を追加しないという点で、提案されたユニオン ソリューションとは異なります。

見る:

IDictionary<TKey, Summary<TKey>> Summarize<TKey, TValue>(IEnumerable<TValue> values) 
{ 
    return values 
        .ToLookup(val => GetKey(val))         // group values by key 
        .Select(x => x.Any() ? x : Enumerable.Repeat(default(TKey), 1).ToLookup(x => GetKey(x)))
        .ToDictionary( 
            group => group.Key, 
            group => CreateSummary(group));   // summarize each group 
} 

ユニオンを使用したソリューションが必要な場合は、同じロジックを使用して、次のような既定のルックアップ テーブルを作成できます。

IDictionary<TKey, Summary<TKey>> Summarize<TKey, TValue>(IEnumerable<TValue> values) 
{ 
    return values 
        .ToLookup(val => GetKey(val))         // group values by key 
        .Union(Enumerable.Repeat(default(TKey), 1).ToLookup(x => GetKey(x)))
        .ToDictionary( 
            group => group.Key, 
            group => CreateSummary(group));   // summarize each group 
} 

お役に立てれば

于 2011-11-08T06:18:12.323 に答える