two multiple fields columns and c# linq group-by

c# - multiple - linq group by count



Cómo optimizar la consulta de linq para agrupar fechas por precio sin fusionar resultados (2)

Tengo esta consulta linq como se indica a continuación. El problema es cuando los datos se agrupan por precio, agrupa las fechas por precio sin considerar un caso donde el mismo precio puede ocurrir para las fechas no consecutivas

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

El resultado es

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

¿Cómo puedo obtener el siguiente resultado?

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


LINQ no es muy adecuado para tales operaciones. El único operador LINQ estándar que se puede usar para realizar dicho procesamiento es Aggregate , pero no es más que un bucle foreach de LINQ-ish:

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

Tenga en cuenta que, a diferencia de muchos métodos LINQ, esto se ejecuta inmediatamente y no regresa hasta que todo el resultado esté listo.


Si crea una clase de ayuda para su DateRange:

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

y el método de ayuda para convertir su lista de fechas:

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

Podrás ordenar tus fechas por períodos:

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