2013-02-07 41 views
2

我想重新創建一個通用版本的Excel函數AVERAGEIF。它被定義爲如下:如何獲得符合標準的通用列表元素的平均值?

返回所有單元格的平均值(算術平均值)在 滿足給定的標準

我的問題是我不知道的範圍如何定義通用平均函數的類型簽名。以下是我有的幾乎可用的代碼。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace ConsTestApp 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var dl = new List<double>() { 10, 10, 0, 0 }; 
      var bl = new List<bool>() { true, true, false, false }; 
      Func<bool, bool> criteria = c => c == true; 

      Console.Out.WriteLine(AVERAGEIF<double, bool>(dl, bl, criteria)); 
      Console.In.Read(); 
     } 

     static T1 AVERAGEIF<T1, T2>(List<T1> average_range, List<T2> criteria_range, Func<T2, bool> applyCriteria) 
     { 
      var cl1 = new List<Container<T1>>(); 
      foreach (var cl in average_range) 
      { 
       cl1.Add(new Container<T1>(cl)); 
      } 
      var cl2 = new List<Container<T2>>(); 
      foreach (var cl in criteria_range) 
      { 
       cl2.Add(new Container<T2>(cl)); 
      } 

      List<Container<T1>> result = 
       new List<Container<T1>>((from d in cl1 
             join b in cl2 
             on d.Id equals b.Id 
             where applyCriteria(b.Value) 
             select new Container<T1> { Value = d.Value, }).ToList<Container<T1>>()); 

      var ret = result.Average<T1>(s => s.Value); 
      // 
      // return ret; 
      return default(T1); 
     } 
    } 

    class Container<T> 
    { 
     private static uint count = 0; 

     public Container() 
      : base() 
     { 
      count++; 
      this.Id = count; 
     } 

     public Container(T t) 
      : this() 
     { 
      this.Value = t; 
     } 

     public T Value 
     { 
      get; 
      set; 
     } 

     public uint Id 
     { 
      get; 
      private set; 
     } 
    } 

    class ContainerList<T> : List<Container<T>> 
    { 
     List<Container<T>> m_list; 

     public ContainerList() 
      : base() 
     { 
      this.m_list = new List<Container<T>>(); 
     } 

     public ContainerList(List<T> l) 
      : this() 
     { 
      foreach (var li in l) 
      { 
       this.m_list.Add(new Container<T>(li)); 
      } 
     } 

     public ContainerList(List<Container<T>> l) 
      : this() 
     { 
      this.m_list = l; 
     } 

     public T Average(Func<Container<T>, T> selector) 
     { 
      T ret = default(T); 


      if (typeof(T) == typeof(double)) 
       ret = (T)(object)(double)0.0; 
      else if (typeof(T) == typeof(decimal)) 
       ret = (T)(object)(decimal)0.0; 
      else 
       ret = default(T); 

      return ret; 
     } 
    } 
} 

回答

11

我想重新創建Excel函數的通用版本AVERAGEIF

你爲什麼不只是使用LINQ?

var average = collection.Where(x => x.Something) 
         .Average(x => x.SomeProperty); 

請注意,如果沒有匹配的元素,這將引發異常。如果你不想這樣做,你可以使用:

var average = collection.Where(x => x.Something) 
         .DefaultIfEmpty() 
         .Average(x => x.SomeProperty); 

目前還不清楚爲什麼你想爲此創建一個單獨的方法。

+0

@slcott還參觀http://msmvps.com/blogs/jon_skeet/archive/tags/Edulinq/default.aspx更多地瞭解如何實現LINQ並獲得更深入的理解。它是由發佈這個答案的人寫的。 –

+0

更多類似這樣的內容,但它的通用實現: '\t var average_range = new List (){10.1,10.1,0.0}; \t \t var criteria_range = new列表(){true,true,false,false}; \t \t Func criteria = c => c == true; \t \t var t1andt2 = average_range.Zip(criteria_range,(l,r)=> Tuple.Create (l,r)); \t \t \t列表 items = t1andt2.Where((x,y)=> criteria(x.Item2))。Select(x => x.Item1).ToList (); \t \t items.Average()。Dump();' – slcott

0

以下是對我的作品的回答:

public double AVERAGEIF<T1,T2>(IEnumerable<T1> average_range, IEnumerable<T2> criteria_range, Func<T2, bool> criteria) 
{ 
    var t1andt2 = average_range.Zip(criteria_range, (l, r) => Tuple.Create<T1, T2>(l, r));  
    return t1andt2.Where(x => criteria(x.Item2)).Average(x => Convert.ToDouble(x.Item1)); 
} 


void Main() 
{ 
    var average_range = new List<string>() {"10.1", "10.1", "0", "0"} ; 
    var criteria_range = new List<bool>() {true, true, false, false }; 
    Func<bool, bool> criteria = c => c == true;  
    AVERAGEIF(average_range, criteria_range, criteria).Dump(); 
}