How can I get the average of the elements of a generic list that meet a criteria?

10,227

I'd like to recreate a generic version of the excel function AVERAGEIF

Why don't you just use LINQ?

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

Note that this will throw an exception if there are no matching elements. If you don't want that, you could use:

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

It's not clear why you would want to create a separate method for this.

Share:
10,227
slcott
Author by

slcott

Updated on July 26, 2022

Comments

  • slcott
    slcott almost 2 years

    I'd like to recreate a generic version of the excel function AVERAGEIF. It is defined as the following:

    Returns the average (arithmetic mean) of all the cells in a range that meet a given criteria

    My problem is that I don't know how to define the type signature of the generic average function. The following is the almost working code that I have.

    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;
            }
        }
    }