1

私はLINQの専門家ではありません。サードパーティから提供された以下のデータがあります:データ

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

このデータは 1 週間、30 日、365 日続きます。

30 分ごとのブロックを 1 時間に変換する必要があります。

例えば

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

Start、End、および Value が 1 つの行になると仮定すると、上記の方法を達成できる方法を誰かが助けることができますか?

4

3 に答える 3

1

このクエリは、指定されたグループでグループ化AggregationTypeでき、2番目のパラメータを使用して不完全なグループを除外できます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 });
            }
        }
    }
}
于 2012-05-08T13:09:43.797 に答える
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));
于 2012-05-08T13:08:00.057 に答える
0

純粋な LINQ を使用してこれに完全にアプローチすることは、実際には非常に困難です。作業を楽にするために、列挙型を変換できるヘルパー メソッドを少なくとも 1 つ記述する必要があります。以下の例を見てください。ここではIEnumerableofを使用し、 2 つの要素を 1 つに結合TimeIntervalするカスタムメソッド (C# イテレータで実装) を用意しています。SplitTuple

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文字列のリストから 30 分ごとの 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ここでは、aを返すカスタム メソッドを使用します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
}

このソリューションの優れている点は、完全に延期されていることです。一度に1行ずつ処理するため、メモリ不足の例外の危険なしに無限に大きなソースを処理できます。これは、与えられた要件を考慮すると重要なようです:

このデータは 1 週間、30 日、365 日続きます。

于 2012-05-08T13:08:39.930 に答える