我想重新创建一个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;
}
}
}
答案 0 :(得分:13)
我想重新创建一个通用版本的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);
目前尚不清楚为什么要为此创建单独的方法。
答案 1 :(得分: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();
}