2012-05-08 134 views
1

我不是LINQ的超级高手,我有一个数据低于由第三方提供: 数据LINQ体30分钟间隔,以小时

Start: 6:00 
End: 6:30 
value: 1 
Start: 7:00 
End: 7:30 
value: 1 
Start: 8:00 
End: 8:30 
value: 1 
Start: 9:00 
End: 9:30 
value: 1 
Start: 10:00 
End: 10:30 
value: 1 
Start: 11:00 
End: 11:30 
value: 1 
Start: 12:00 
End: 12:30 
value: 1 
Start: 13:00 
End: 13:30 
value: 1 
Start: 14:00 
End: 14:30 
value: 1 
... 
Start: 05:00 
End: 05:30 
value: 1 

这一数据不断去了一个星期,然后30天和365天。

我需要将每个30分钟的块转换为一个小时。

e.g

Start: 6:00 
End: 7:00 
Value: 2 
Start:7:00 
End: 8:00 
Value:2 
...... 

假设开始,结束和价值当属一排,可能有人帮助了如何通过上述可以实现吗?

+0

我在第二个例子中注意到,该值为2.您是否改变了最终值和添加下一个的当前值? –

+0

你的例子给出了每次30分钟的差距。那是对的吗? 'End'总是'**:30'和'Start'总是'**:00' – hwcverwe

+0

我不同意闭幕者关闭这个问题,因为它太过本地化。问题是关于解析几种类型的项目的结构集合,这给了其他访问者非常有趣。但是,这个问题实际上表达得很差,这将是一个很好的理由。 – Steven

回答

1

该查询能够按给定的AggregationType进行分组,并且能够使用第二参数checkType过滤掉不完整的组。

private enum AggerationType { Year = 1, Month = 2, Day = 3, Hour = 4 } 

private IList<Data> RunQuery(AggerationType groupType, AggerationType checkType) 
{ 
    // The actual query which does to trick 
    var result = 
     from d in testList 
     group d by new { 
      d.Start.Year, 
      Month = (int)groupType >= (int)AggerationType.Month ? d.Start.Month : 1, 
      Day = (int)groupType >= (int)AggerationType.Day ? d.Start.Day : 1, 
      Hour = (int)groupType >= (int)AggerationType.Hour ? d.Start.Hour : 1 
     } into g 
     // The where clause checks how much data needs to be in the group 
     where CheckAggregation(g.Count(), checkType) 
     select new Data() { Start = g.Min(m => m.Start), End = g.Max(m => m.End), Value = g.Sum(m => m.Value) }; 

    return result.ToList(); 
} 

private bool CheckAggregation(int groupCount, AggerationType checkType) 
{ 
    int requiredCount = 1; 
    switch(checkType) 
    { 
     // For year all data must be multiplied by 12 months 
     case AggerationType.Year: 
      requiredCount = requiredCount * 12; 
      goto case AggerationType.Month; 
     // For months all data must be multiplied by days in month 
     case AggerationType.Month: 
      // I use 30 but this depends on the given month and year 
      requiredCount = requiredCount * 30; 
      goto case AggerationType.Day; 
     // For days all data need to be multiplied by 24 hour 
     case AggerationType.Day: 
      requiredCount = requiredCount * 24; 
      goto case AggerationType.Hour; 
     // For hours all data need to be multiplied by 2 (because slots of 30 minutes) 
     case AggerationType.Hour: 
      requiredCount = requiredCount * 2; 
      break; 

    } 
    return groupCount == requiredCount; 
} 

在这里,如果你想要一些测试数据:

class Data 
{ 
    public DateTime Start { get; set; } 
    public DateTime End { get; set; } 
    public int Value { get; set; } 
} 

// Just setup some test data simulary to your example 
IList<Data> testList = new List<Data>(); 
DateTime date = DateTime.Parse("6:00"); 

// This loop fills just some data over several years, months and days 
for (int year = date.Year; year > 2010; year--) 
{ 
    for(int month = date.Month; month > 0; month--) 
    { 
     for (int day = date.Day; day > 0; day--) 
     { 
      for(int hour = date.Hour; hour > 0; hour--) 
      { 
       DateTime testDate = date.AddHours(-hour).AddDays(-day).AddMonths(-month).AddYears(-(date.Year - year)); 
       testList.Add(new Data() { Start = testDate, End = testDate.AddMinutes(30), Value = 1 }); 
       testList.Add(new Data() { Start = testDate.AddMinutes(30), End = testDate.AddHours(1), Value = 1 }); 
      } 
     } 
    } 
} 
+0

感谢@hwcverwe,如果我们有多个日期,解决方案似乎会中断。 –

+0

@nilpun。我的答案现在包含一个查询,它可以在多天内完成技巧 – hwcverwe

+0

非常感谢@hwcverwe。无论如何,我们可以使用这个技巧来获得给定数据的日,周,月和年的汇总值吗? –

0

以下是代码。这似乎有点丑,因为switch声明。重构它会更好,但它应该显示这个想法。

var items = input.Split('\n'); 

Func<string, string> f = s => 
{ 
    var strings = s.Split(new[] {':'}, 2); 
    var key = strings[0]; 
    var value = strings[1]; 

    switch (key.ToLower()) 
    { 
     case "start": 
      return s; 
     case "value": 
      return String.Format("{0}: {1}", key, Int32.Parse(value) + 1); 
     case "end": 
      return String.Format("{0}: {1:h:mm}", key, 
       DateTime.Parse(value) + 
       TimeSpan.FromMinutes(30)); 
     default: 
      return ""; 
    } 
}; 

var resultItems = items.Select(f); 

Console.Out.WriteLine("result = {0}", 
          String.Join(Environment.NewLine, resultItems)); 
0

实际上很难完全用纯LINQ来解决这个问题。为了使生活更轻松,您需要编写至少一个帮助器方法,该方法允许您转换枚举。看看下面的例子。在这里,我使用TimeInterval一个IEnumerable的和有一个自定义Split方法(用C#迭代器实现的),在一个Tuple联接两个元件一起:

class TimeInterval 
{ 
    DateTime Start; 
    DateTime End; 
    int Value; 
} 

IEnumerable<TimeInterval> ToHourlyIntervals(
    IEnunumerable<TimeInterval> halfHourlyIntervals) 
{ 
    return 
     from pair in Split(halfHourlyIntervals) 
     select new TimeInterval 
     { 
      Start = pair.Item1.Start, 
      End = pair.Item2.End, 
      Value = pair.Item1.Value + pair.Item2.Value 
     }; 
} 

static IEnumerable<Tuple<T, T>> Split<T>(
    IEnumerable<T> source) 
{ 
    using (var enumerator = source.GetEnumerator()) 
    { 
     while (enumerator.MoveNext()) 
     { 
      T first = enumerator.Current; 

      if (enumerator.MoveNext()) 
      {    
       T second = enumerator.Current; 
       yield return Tuple.Create(first, second); 
      } 
     } 
    } 
} 

同样可以适用于该问题的第一部分(从字符串列表)中提取每半小时TimeInterval S:

IEnumerable<TimeInterval> ToHalfHourlyIntervals(
    IEnumerable<string> inputLines) 
{ 
    return 
     from triple in TripleSplit(inputLines) 
     select new TimeInterval 
     { 
      Start = DateTime.Parse(triple.Item1.Replace("Start: ", "")), 
      End = DateTime.Parse(triple.Item2.Replace("End: ", "")), 
      Value = Int32.Parse(triple.Item3) 
     }; 
} 

这里我使用一个自定义的TripleSplit方法返回的Tuple<T, T, T>(这将是容易写)。有了这个,完整的解决方案看起来像这样:

// Read data lazilzy from disk (or any other source) 
var lines = File.ReadLines(path); 

var halfHourlyIntervals = ToHalfHourlyIntervals(lines); 

var hourlyIntervals = ToHourlyIntervals(halfHourlyIntervals); 

foreach (var interval in hourlyIntervals) 
{ 
    // process 
} 

这个解决方案的好处是它完全延迟。这一次处理一行,它允许您处理无限期地大源没有任何内存不足的例外,这似乎是重要的危险考虑您给出的要求:

这一数据不断去了一个星期,然后30天和365天。

+0

谢谢@Steven,您能否展示我们如何转换为日,周,月和年。 –

相关问题