2016-03-18 5 views
0

汎用TopN IEnumerable<T>拡張を追加しようとしています。TopNをIEnumerable拡張子

パラメータが正の場合は、Take()と同じですが、負の場合はTake()と同じ操作を実行する必要がありますが、Take()の最後の値と一致する値をすぐに返します。 (SQL TOPとしてのn TIES WITH同じ)

これは、現時点で私が持っているコードです: -

public static class Test 
{ 
    public static IEnumerable<TSource> TopN<TSource>(this IEnumerable<TSource> source, int topN) 
    { 
     return TopN(source, topN, (v1, v2) => v1.Equals(v2)); 
    } 

    public static IEnumerable<TSource> TopN<TSource>(this IEnumerable<TSource> source, int topN, Func<TSource, TSource, bool> comparer) 
    { 
     if (source == null) throw new ArgumentNullException(nameof(source)); 
     if (comparer == null) throw new ArgumentNullException(nameof(comparer)); 

     return topN >= 0 
      ? source.Take(topN) 
      : TopNWithTiesIterator(source, -topN, comparer); 
    } 

    static IEnumerable<TSource> TopNWithTiesIterator<TSource>(this IEnumerable<TSource> source, int topN, Func<TSource, TSource, bool> comparer) 
    { 
     var lastItem = default(TSource); 

     foreach (var item in source) 
     { 
      if (topN-- > 0 || comparer(item, lastItem)) 
      { 
       lastItem = item; 

       yield return item; 
      } 
      else 
      { 
       yield break; 
      } 
     } 
    } 
} 

、ここでは、実世界の使用法のサンプルと、私が試した他のいくつかの簡単なテストであります:

 if (TopN != 0) 
     { 
      var values = new[] { 1, 2, 2, 3 }; 
      Debug.Assert(!values.TopN(0).Any()); 
      Debug.Assert(!values.TopN(0, (v1, v2) => v1 == v2).Any()); 

      Debug.Assert(values.TopN(1, (v1, v2) => v1 == v2).Count() == 1); 
      Debug.Assert(values.TopN(-1, (v1, v2) => v1 == v2).Count() == 1); 

      Debug.Assert(values.TopN(2, (v1, v2) => v1 == v2).Count() == 2); 
      Debug.Assert(values.TopN(-2, (v1, v2) => v1 == v2).Count() == 3); 

      Debug.Assert(values.TopN(2).Count() == 2); 
      Debug.Assert(values.TopN(-2).Count() == 3); 

      // This is how I really want to use it 
      summaries = summaries.TopN(TopN, (v1, v2) => v1.ClientValue + v1.AdviserValue == v2.ClientValue + v2.AdviserValue); 
     } 

私の質問は、比較者としてFunc<TSource, TSource, bool>を使用することが正しいかどうかについてです。

IEqualityComparer<T>またはIEquatable<<T>などを使用してください。

+0

この質問を意見に基づいて言い換えようとするのは難しいです。 「既存のMicrosoft標準に合致させるために」追加した場合に役立ちますか? –

答えて

0

Func<TSource, TSource, bool>IEqualityComparer<T>の間には選択肢がありません。ここではデフォルトオブジェクトのEquals比較者(v1, v2) => v1.Equals(v2)を使用しています。

これは、Func<>またはオプションのメソッド引数として直接カスタム比較関数を提供するかどうかによって異なります。

+0

良い点。私は、Funcオプションが必要なという結論に至りました。私が現在使っているのは匿名型のため、IEquatableやIEqualityComparerを実装できないからです。 –

1

デフォルトと期待される動作は以下のようになります。何IEqualityComparer<T>が提供されていない

  • 場合は、あなたの方法はTSourceIEquatable<T>を実装しているかどうかをチェックし、IEquatable<T>.Equals(T)を使用する必要があります。それ以外の場合は、Object.Equalsを使用してください。

  • IEqualityComparer<T>が提供されている場合は、それを使用してください。

  • IEqualityComparer<T>の動作を模倣するために述語が提供されている場合は、IEqualityComparer<T>の代わりに使用してください。

+0

以下のコメントを参照してください。私は、匿名型はFuncオプションが残っていなければならないことを意味すると思いますが、私の単純化されたEqualsではなくIEquatable/Object.Equalsを使用するための提案も実装します。あなたのおかげで両方。 –

+0

@SimonHewittええ、あなたは多くの過負荷を提供することができます。しかし、あなたが "完全な"解決策を望むなら、あなたは私の答えの基準に従うべきです –