2015-05-15 14 views
8

Muszę zgrupować moje dane przez każdą minutę dnia i uzyskać liczbę zdarzeń, które miały miejsce w tej minucie. Obecnie mam:C# uzyskać minutę od daty w grupie linq przez

   items.GroupBy(x => new 
       { 
        x.date.Minute, 
        x.date.Hour 
       }) 
       .Select(x => new TransferObject 
       { 
        Minute = x.Key.Minute, 
        Hour = x.Key.Hour, 
        Count = x.Count(), 
        Day = date 
       }).OrderBy(x => x.Hour).ThenBy(x => x.Minute).ToList(); 

to robi to, co muszę, ale problemem jest to, że nie może mieć punktów danych dla każdej chwili, w jaki sposób mogę dodać 0 do Count pole jeżeli nie mam danych dla tej chwili ? Ewentualnie mogę dodać numer minutowy (0 ... 1440) i później uzupełnić brakujące wartości.

EDIT

Rozwiązanie obecnie grupy jedynie datą startu, ale że obiekt rzeczywiście ma pole end_date. Zasadniczo w tej chwili mam wszystkie wydarzenia, które rozpoczęły się w tej chwili, ale muszę uzyskać liczbę zdarzeń, które były w tej chwili uruchomione.

Dane Mam zawiera:

date     end_date 
2015-05-15 09:52:15.650 2015-05-15 09:55:38.097 
2015-05-15 09:52:15.633 2015-05-15 09:52:16.097 
2015-05-15 09:52:11.633 2015-05-15 09:52:13.047 
2015-05-15 09:51:49.097 2015-05-15 09:55:17.687 
2015-05-15 09:51:49.087 2015-05-15 09:56:17.510 

W tej chwili nie użyć pola end_date więc wyjście jest

{Count:2;Hour:9;Minute:51} 
{Count:3;Hour:9;Minute:52} 

muszę mieć wszystkie zdarzenia, które zostały uruchomione, coś

{Count:2;Hour:9;Minute:51} 
{Count:5;Hour:9;Minute:52} 
{Count:3;Hour:9;Minute:53} 
{Count:3;Hour:9;Minute:54} 
{Count:3;Hour:9;Minute:55} 
{Count:2;Hour:9;Minute:56} 
+0

Zobacz zaktualizowaną odpowiedź. – Jamiec

Odpowiedz

1

Po pierwsze można wygenerować całą dobę & minut w ciągu dnia za pomocą kilku prostych LINQ:

var all = Enumerable.Range(0,24) 
        .SelectMany(x => Enumerable.Range(0,60), 
           (x,y) => new {Hr = x, Min=y}); 
foreach(var a in all) 
    Console.WriteLine("{0}:{1}",a.Hr,a.Min); // 00:00 thru 23:59 

żywo przykład: http://rextester.com/NPNKN82691

Można wtedy skorzystać z tej kolekcji dołączyć do oryginalnych wyników w celu utworzenia ostatecznej wersji:

var all = Enumerable.Range(0,24) 
        .SelectMany(x => Enumerable.Range(0,60), 
           (x,y) => new {Hr = x, Min=y}); 

var items = new[]{ 
     new Item{Date = new DateTime(2015,01,01,0,1,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,3,0)} 
}; 

var results = all.GroupJoin(items, 
          x => new {Hour = x.Hr,Minute = x.Min}, 
          y => new {Hour = y.Date.Hour,Minute = y.Date.Minute}, 
          (x,y) => new {Hour = x.Hr, Minute = x.Min, Count = y.Count()}); 

foreach(var result in results) 
    Console.WriteLine("{0:00}:{1:00} = {2}",result.Hour, result.Minute, result.Count); 

wyjściowy

00:00 = 0
00:01 = 6
00:02 = 0
00:03 = 1
00:04 = 0
//..snip ..//
23:55 = 0
23:56 = 0
23:57 = 0
23:58 = 0
23:59 = 0

żywo przykładem http://rextester.com/OAYZ95244


EDIT

Biorąc pod uwagę Twój chan ge wymogu rozważyć zakres dat, można dodać nową klasę i metodę ekstrapolacji godzin & minutę z każdej pozycji:

// classes used 
public class ExtrapolatedItem 
{ 
    public int Hour{get;set;} 
    public int Minute{get;set;} 
} 

public class Item{ 
    public DateTime Date{get;set;} 
    public DateTime EndDate{get;set;} 
} 

// method 

private static IEnumerable<ExtrapolatedItem> Extrapolate(Item item) 
{ 
    for(var d = item.Date;d<=item.EndDate; d = d.AddMinutes(1)) 
    { 
     yield return new ExtrapolatedItem{ Hour = d.Hour, Minute = d.Minute}; 
    } 
} 

ten może być następnie wykorzystane w oryginalnej metody takie jak to:

var items = new[]{ 
     new Item{Date = new DateTime(2015,01,01,0,1,0),EndDate = new DateTime(2015,1,1,0,5,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0),EndDate = new DateTime(2015,1,1,0,5,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0),EndDate = new DateTime(2015,1,1,0,5,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0),EndDate = new DateTime(2015,1,1,0,5,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0),EndDate = new DateTime(2015,1,1,0,5,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,1,0),EndDate = new DateTime(2015,1,1,0,5,0)}, 
     new Item{Date = new DateTime(2015,01,01,0,3,0),EndDate = new DateTime(2015,1,1,0,5,0)} 
}; 
var results = all.GroupJoin(items.SelectMany(Extrapolate), // extrapolate each item to a list of extrapolated items 
          x => new {Hour = x.Hr,Minute = x.Min}, 
          y => new {Hour = y.Hour,Minute = y.Minute}, 
          (x,y) => new {Hour = x.Hr, Minute = x.Min, Count = y.Count()}); 

wyjście

00:00 = 0
00:01 = 6
00:02 = 6
00:03 = 7
00:04 = 7
00:05 = 7
00:06 = 0
00:07 = 0
//..snip. .//

żywo przykład: http://rextester.com/VOVJCS56741

4

wydaje się, że chcesz GroupJoin() nie tylko GroupBy(), coś takiego:

var source = ... // Your query; 

    var result = Enumerable 
    .Range(0, 1440) // All the minutes that should be in the result 
    .GroupJoin(source, 
     minutes => minutes, 
     item => item.Hour * 60 + item.Minute, 
     (minutes, items) => new { 
      hour = minutes/60, 
      minute = minutes % 60, 
      count = items.Count() }); // Count() can well return 0... 
0

To powinno działać bez join. Przyjmij tylko jedną datę w pozycjach.

var date = items.Select(i => i.date).Min(); 

Enumerable.Range(0, 60 * 24) 
.Select(i => 
    new 
    { 
     Minute = i % 60, 
     Hour = i/60, 
     Day = date, 
     Count = items.Where(j => j.date.Hour = i/60 && j.date.Minute = i % 60).Count() 
    }); 
+0

To będzie bardzo nieefektywne; efektywne wyliczanie całych "przedmiotów" za każdą minutę w ciągu dnia (1440 razy). Jeśli 'items' jest mały, prawdopodobnie nie ma problemu, ale jeśli jego duży - eek! – Jamiec