如何获取满足条件的泛型列表元素的平均值?

3
我想重新创建一个通用版本的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;
        }
    }
}
2个回答

16

我想重新创建一个通用版本的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);

不清楚为什么您希望为此创建单独的方法。


@slcott 还可以访问 http://msmvps.com/blogs/jon_skeet/archive/tags/Edulinq/default.aspx 了解更多关于如何实现LINQ并深入理解它的知识。这篇文章是由发布此答案的那个人撰写的。 - Aniket Inge
类似这样,但是它的实现是通用的: var average_range = new List<double>() {10.1, 10.1, 0, 0} ; var criteria_range = new List<bool>() {true, true, false, false }; Func<bool, bool> criteria = c => c == true; var t1andt2 = average_range.Zip(criteria_range, (l, r) => Tuple.Create<double, bool>(l, r)); List<double> items = t1andt2.Where((x,y) => criteria(x.Item2)).Select(x => x.Item1).ToList<double>(); 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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接