How to optimize linq query to group dates by price without merging results

I have this linq request as below. The problem is when data is grouped by price, it groups dates by price, not considering the case when the same price may occur for inappropriate dates.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        //Console.WriteLine("Hello World");
        List<Prices> list = new List<Prices>();
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-17"), Price = Double.Parse("50")});
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-18"), Price = Double.Parse("50")});
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-19"), Price = Double.Parse("50")});
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-20"), Price = Double.Parse("100")});
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-21"), Price = Double.Parse("100")});
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-22"), Price = Double.Parse("100")});
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-23"), Price = Double.Parse("50")});
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-24"), Price = Double.Parse("50")});
        list.Add(new Prices() { Date = DateTime.Parse("2017-06-25"), Price = Double.Parse("50")});


        var baseservices = list
                .GroupBy(l => l.Price)
                .Select(g => new
                {
                    Price = g.Key,
                    PeriodStart = g.Select(l=>l.Date).Min(),
                    PeriodEnd = g.Select(l => l.Date).Max(),
                });

        foreach(var item in baseservices)
        {
            Console.WriteLine(item.Price + " " + item.PeriodStart + " " + item.PeriodEnd);  
        }

    }
}

public class Prices
{
    public DateTime Date {get;set;}
    public double Price {get;set;}  
}

public class Quote
{
    public DateTime PeriodStart {get;set;}
    public DateTime PeriodEnd {get;set;}
    public double Price {get;set;}  
}

Result

50 6/17/2017 12:00:00 AM 6/25/2017 12:00:00 AM
100 6/20/2017 12:00:00 AM 6/22/2017 12:00:00 AM

How can I get the following result

50 6/17/2017 12:00:00 AM 6/29/2017 12:00:00 AM
100 6/20/2017 12:00:00 AM 6/22/2017 12:00:00 AM
50 6/23/2017 12:00:00 AM 6/25/2017 12:00:00 AM
+1
source share
2 answers

LINQ is not suitable for such operations. The only standard LINQ operator that can be used to perform this kind of processing is Aggregate, but it is nothing more than a LINQ-ish loop.foreach

var baseservices = list
    .OrderBy(e => e.Date)
    .Aggregate(new List<Quote>(), (r, e) =>
    {
        if (r.Count > 0 && r[r.Count - 1].Price == e.Price && r[r.Count - 1].PeriodEnd.AddDays(1) == e.Date)
            r[r.Count - 1].PeriodEnd = e.Date;
        else
            r.Add(new Quote { Price = e.Price, PeriodStart = e.Date, PeriodEnd = e.Date });
        return r;
    });

, LINQ, , .

+3

DateRange:

public class  DateRange
{
    public DateTime PeriodStart { get; set; }
    public DateTime PeriodEnd { get; set; }
}

:

public static IEnumerable<DateRange> Convert(IEnumerable<DateTime> dates)
{
    var ret = new DateRange();
    foreach (var date in dates)
    {
        if (ret.PeriodEnd == default(DateTime))
        {
            ret.PeriodStart = date;
            ret.PeriodEnd = date;
        }
        else if (ret.PeriodEnd.AddDays(1) == date)
        {
            ret.PeriodEnd = date;
        }
        else
        {
            yield return ret;
            ret = new DateRange();
        }
    }
    yield return ret;
 }

:

var baseservices = list
        .GroupBy(l => l.Price)
        .Select(g => new
        {
            Price = g.Key,
            Dates = Convert(g.Select(d=>d.Date)).ToList()
        })
       .SelectMany(r=>r.Dates, (a,b)=>new Quote {
                              Price = a.Price, 
                              PeriodStart = b.PeriodStart, 
                              PeriodEnd = b.PeriodEnd})
       .ToList();
+1

Source: https://habr.com/ru/post/1667773/


All Articles