How to optmize linq query for grouping dates by price without merging results

蓝咒 提交于 2019-12-24 07:35:58

问题


I have this linq query as stated below. The problem is when the data is grouped by price, it groups dates by price without considering a case where same price can occur for nonconsecutive 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;}  
}

The result is

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:


LINQ is not well suited for such operations. The only standard LINQ operator that can be used to do such processing is Aggregate, but it's no more than LINQ-ish foreach loop:

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

Note that in contrast with many LINQ methods, this executes immediate and does not return until the whole result is ready.




回答2:


If you create help class for your DateRange:

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

and help method to convert your list of dates:

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

You will be able to sort your dates to periods:

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();


来源:https://stackoverflow.com/questions/41653586/how-to-optmize-linq-query-for-grouping-dates-by-price-without-merging-results

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!