pmunin
1/4/2016 - 3:02 PM

Aggregate several accumulators within one enumerator run

Aggregate several accumulators within one enumerator run

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

/// <summary>
/// IEnumerable extension that allows to aggregate multiple accumulators within single enumerator run
/// Latest version is here: https://gist.github.com/pmunin/0f4900c57a183b6f0083
/// </summary>
internal static class AggregateManyExtensions
{
    public static void Example()
    {
        var items = new[] { 9, 5, 2, 7, 3, 8, 3, 1 };
        //Func<int, int, int> maxFunc = (item, seed) => Math.Max(item, seed);
        //Func<int, int, int> sumFunc = (item, seed) => item + seed;
        //Func<string, int, string> concatFunc = (seed, item) => string.Concat(item.ToString(), seed);

        var x1 = items.Aggregate(0, (s, i) => Math.Max(s, i));
        var x2 = items.Aggregate(0, (s, i) => s+i );
        var x3 = items.Aggregate("", (s, i) => s + i.ToString());


        var totals = items.AggregateMany()
            .By((s, i) => Math.Max(s, i), 0)
            .By((s, i) => s + i, 0)
            .By((s, i) => s + i.ToString(), "")
            .ToArray()
            ;
        ;
    }

    public class AggregateManyHost<TItem> : IEnumerable<object>
    {
        public IEnumerable<TItem> Items { get; set; }
        public List<Aggregator<TItem>> Aggregators = new List<Aggregator<TItem>>();

        bool aggregationDone = false;
        public void Aggregate(bool reaggregate=true)
        {
            Aggregators.ForEach(a=>a.Reset());
            if (aggregationDone&&(!reaggregate)) return;
            Items.Aggregate(Aggregators, (aggs, i)=> {
                aggs.ForEach(a => a.ProcessItem(i));
                return aggs;
            });
            aggregationDone = true;
        }

        public IEnumerator<object> GetEnumerator()
        {
            Aggregate();
            return Aggregators.Select(a=>a.GetResult()).Cast<object>().GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public abstract class Aggregator<TItem>
    {
        public abstract dynamic GetResult();
        public abstract void ProcessItem(TItem item);
        public abstract void Reset();
    }

    public class Aggregator<TItem, TAccumulate>:Aggregator<TItem>
    {
        public TAccumulate Result { get; set; }
        public TAccumulate InitValue { get; set; }
        public override dynamic GetResult()
        {
            return Result;
        }
        public Func<TAccumulate, TItem, TAccumulate> aggregateFunc { get; set; }
        public override void ProcessItem(TItem item)
        {
            Result = aggregateFunc(Result, item);
        }
        public override void Reset()
        {
            Result = InitValue;
        }


    }

    public static AggregateManyHost<TItem> AggregateMany<TItem>(this IEnumerable<TItem> items)
    {
        return new AggregateManyHost<TItem>() { Items = items };
    }

    public static AggregateManyHost<TItem> By<TItem, TAccumulator>(this AggregateManyHost<TItem> aggregateMany
        , Func<TAccumulator, TItem, TAccumulator> aggregateFunc, TAccumulator seed = default(TAccumulator))
    {
        aggregateMany.Aggregators.Add(new Aggregator<TItem, TAccumulator>() { aggregateFunc = aggregateFunc, InitValue = seed });
        return aggregateMany;
    }

    public static Func<TAccumulator> GetBy<TItem, TAccumulator>(this AggregateManyHost<TItem> aggregateMany
        , Func<TAccumulator, TItem, TAccumulator> aggregateFunc, TAccumulator seed = default(TAccumulator))
    {
        aggregateMany.By(aggregateFunc, seed);
        var aggregator = aggregateMany.Aggregators.Last() as Aggregator<TItem, TAccumulator>;
        return () =>
        {
            aggregateMany.Aggregate(false);
            return aggregator.Result;
        };
    }


}