views:

106

answers:

3

I have next very non-optimized code:

void Summarize(IEnumerable<Section> sections)
{
    this.DateBegin = sections.Select(s => s.Date).Min();
    this.DateEnd = sections.Select(s => s.Date).Max();
    this.Income = sections.Where(s => s.IsIncome).Sum(r => r.Amount);
    this.ExpenditureBank = sections.Where(s => s.IsExpenditureBank).Sum(r => r.Amount);
    this.ExpenditureClient = sections.Where(s => s.IsExpenditureClient).Sum(r => r.Amount);
    this.Expenditure = this.ExpenditureBank + this.ExpenditureClient;
}

How to rewrite such it using IEnumerable.Aggregate(), if applicable?

+1  A: 
void Summarize(IEnumerable<Section> sections)
{
    this.DateBegin = sections.Select(s => s.Date).Min();
    this.DateEnd = sections.Select(s => s.Date).Max();
    this.Income = sections.Where(s => s.IsIncome).Sum(r => r.Amount);
    this.Expenditure = sections.Aggregate(0, (agg, next) => 
        agg += (next.IsExpenditureBank ? next.Amount : 0) +
            (next.IsExpenditureClient ? next.Amount : 0));
}

How's that?

EDIT:

Ok, I've had a rethink, take a look:

void Summarize(IEnumerable<Section> sections)
{
    var result = sections.Aggregate(new
    {
        DateBegin = DateTime.MaxValue,
        DateEnd = DateTime.MinValue,
        Income = 0,
        Expenditure = 0
    },
        (agg, next) =>
        new
        {
            DateBegin = next.Date < agg.DateBegin ? next.Date : agg.DateBegin,
            DateEnd = next.Date > agg.DateEnd ? next.Date : agg.DateEnd,
            Income = agg.Income + (next.IsIncome ? next.Amount : 0),
            Expenditure = agg.Expenditure + (next.IsExpenditureBank ? next.Amount : 0) +
                (next.IsExpenditureClient ? next.Amount : 0)
        }
    );
}

Beware of errors if sections is empty.

Codesleuth
Here http://stackoverflow.com/questions/2132615/find-minimal-and-maximun-date-in-array-using-linq/2137362#2137362 is Aggregate() for dates. Is it possible to combine Aggregate() for dates and for numbers?
abatishchev
Updated answer, take a look.
Codesleuth
+3  A: 

Sometimes a good old fashioned foreach loop is the best tool for the job. I think that's the case here otherwise you're either going to be enumerating the sections multiple times or have some very unreadable LINQ code.

And although you probably know ahead of time that the sections parameter will be an array or collection or what not, it might be something that is very expensive to enumerate or something that doesn't yield consistent values between enumerations. IEnumerable can surprise you sometimes.

Josh Einstein
+1  A: 

my solution is very close to Codesleuth's but would first define a Summary Struct

struct Summary 
{
    public DateTime DateBegin {get; set;}
    public DateTime DateEnd {get; set;}
    public int Income  {get; set;}
    public int ExpenditureBank {get; set;}
    public int ExpenditureClient {get; set;}
    public int Expenditure {get {return ExpenditureBank+ExpenditureClient; }}
}

void Summarize(IEnumerable<Section> sections)
{
    var result = sections.Aggregate(new Summary
    {
        DateBegin = DateTime.MaxValue,
        DateEnd = DateTime.MinValue,
        Income = 0,
        ExpenditureBank =0,
        ExpenditureClient =0,
    },
    (agg, next) =>
    new Summary
    {
        DateBegin = next.Date < agg.DateBegin ? next.Date : agg.DateBegin,
        DateEnd = next.Date > agg.DateEnd ? next.Date : agg.DateEnd,
        Income = agg.Income + (next.IsIncome ? next.Amount : 0),
        ExpenditureBank = next.IsExpenditureBank ? next.Amount: 0,
        ExpenditureClient = next.IsExpenditureClient ? next.Amount : 0
    });
}

Note that the struct would ideally be immutable.

Johnny Blaze
Basically the same as my answer. However you haven't combined the results of the amount when `ExpenditureBank` or `ExpenditureClient` are true - which the question inevitably does. Your answer could be simplified for this.
Codesleuth
Can I just point out that both our answers are extremely inefficient. Creating a new object for every instance of `Sections` in the `sections` argument is just totally wasteful. The question is very impractical. However, our answers **are** correct.
Codesleuth
well it depends by creating a new object we can take advantage of the fact that the agragation is associative so we could divide the work on multiple thread and reduce each individual result at the end using the same code basicaly wich could yield a better performance. Plus the i did choose a struct instead of class for efficiency reason anonymous type are implemented as classes by default so your answer might be less efficient that mine ;)
Johnny Blaze
Well according to http://stackoverflow.com/questions/2132615/find-minimal-and-maximun-date-in-array-using-linq/2137362#2137362 you told him you with accept his answer. What happened?
citronas