如何按汇总分组?

时间:2019-06-12 12:44:36

标签: c# linq group-by overlapping

我有以下数据:

ATMID    StatusId  ComponentId   FromDateTime          ToDateTime
4           1          3        2019-01-01 18:20     2019-01-01 18:45
4           2          5        2019-01-01 18:25     2019-01-01 18:45
5           3          2        2019-01-01 18:10     2019-01-01 18:45 
5           1          2        2019-01-01 18:00     2019-01-01 18:45
5           6          5        2019-01-01 18:46     2019-01-01 18:50

我希望以这样的方式汇总此结果:

Atmid    St_Comp         FromDateTime          ToDateTime
  4    {(1,3),(2,5)}    2019-01-01 18:20      2019-01-01 18:45 
  5    {(3,2),(1,2)}    2019-01-01 18:00      2019-01-01 18:45 
  5    {(6,5)}          2019-01-01 18:46      2019-01-01 18:50 

如何使用Linq实现此目的?

这是我的代码:

var grouped = Problem_Fact_Measure_Overalls.GroupBy(
                  i => new { i.ATMId},
                  i => new {i.ComponentId,i.StatusId},
                  (Atmid, St_Comp) => new { Atmid, St_Comp });

但是我无法汇总FromDateTime的最小值。

2 个答案:

答案 0 :(得分:2)

需要使用Aggregate这样的方法:

var result = data
    .Aggregate(
        new List<KeyValuePair<DateTimeRange, List<AtmRecord>>>(),
        (accumulator, atmRecord) => {
            var segment = accumulator.FirstOrDefault(
                v => atmRecord.ATMID == v.Value.First().ATMID && 
                    atmRecord.FromDateTime <= v.Key.To &&
                    atmRecord.ToDateTime >= v.Key.From);

            if (segment.Key == null)
            {
                accumulator.Add(new KeyValuePair<DateTimeRange, List<AtmRecord>>(
                    new DateTimeRange(atmRecord.FromDateTime, atmRecord.ToDateTime),
                    new List<AtmRecord>() {atmRecord}));

                return accumulator; 
            }

            if (atmRecord.FromDateTime < segment.Key.From)
            {
                segment.Key.From = atmRecord.FromDateTime;
            }

            if (atmRecord.ToDateTime > segment.Key.To)
            {
                segment.Key.To = atmRecord.ToDateTime;
            }                        

            segment.Value.Add(atmRecord);

            return accumulator;
        },
        accumulator => {
            return accumulator
                .Select(v => new 
                {
                    Atmid = v.Value.First().ATMID,
                    St_Comp = v.Value
                        .Select(r => (r.StatusId, r.ComponentId))
                        .ToArray(),
                    FromDateTime = v.Key.From,
                    ToDateTime = v.Key.To
                });
        })
        .ToArray();

/* result: 
[0]:{ Atmid = 4, St_Comp = {(int, int)[2]}, FromDateTime = {2019-01-01 6:20:00 p.m.}, ToDateTime = {2019-01-01 6:45:00 p.m.} }
Atmid [int]:4
FromDateTime [DateTime]:{2019-01-01 6:20:00 p.m.}
St_Comp:{(int, int)[2]}
[0]:(1, 3)
[1]:(2, 5)
ToDateTime [DateTime]:{2019-01-01 6:45:00 p.m.}

[1]:{ Atmid = 5, St_Comp = {(int, int)[2]}, FromDateTime = {2019-01-01 6:00:00 p.m.}, ToDateTime = {2019-01-01 6:45:00 p.m.} }
Atmid [int]:5
FromDateTime [DateTime]:{2019-01-01 6:00:00 p.m.}
St_Comp:{(int, int)[2]}
[0]:(3, 2)
[1]:(1, 2)
ToDateTime [DateTime]:{2019-01-01 6:45:00 p.m.}

[2]:{ Atmid = 5, St_Comp = {(int, int)[1]}, FromDateTime = {2019-01-01 6:46:00 p.m.}, ToDateTime = {2019-01-01 6:50:00 p.m.} }
Atmid [int]:5
FromDateTime [DateTime]:{2019-01-01 6:46:00 p.m.}
St_Comp:{(int, int)[1]}
[0]:(6, 5)
ToDateTime [DateTime]:{2019-01-01 6:50:00 p.m.}
 */

测试数据:

var data = new[]{ 
    new AtmRecord( 4, 1, 3, DateTime.Parse("2019-01-01 18:20"), DateTime.Parse("2019-01-01 18:45") ),
    new AtmRecord( 4, 2, 5, DateTime.Parse("2019-01-01 18:25"), DateTime.Parse("2019-01-01 18:45") ),
    new AtmRecord( 5, 3, 2, DateTime.Parse("2019-01-01 18:10"), DateTime.Parse("2019-01-01 18:45") ),
    new AtmRecord( 5, 1, 2, DateTime.Parse("2019-01-01 18:00"), DateTime.Parse("2019-01-01 18:45") ),
    new AtmRecord( 5, 6, 5, DateTime.Parse("2019-01-01 18:46"), DateTime.Parse("2019-01-01 18:50") )
};

其他类别:

internal sealed class AtmRecord
{
    public int ATMID { get; }
    public int StatusId { get; }
    public int ComponentId { get; }
    public DateTime FromDateTime { get; }
    public DateTime ToDateTime { get; }

    public AtmRecord(int aTMID, int statusId, int componentId, DateTime fromDateTime, DateTime toDateTime)
    {
        ATMID = aTMID;
        StatusId = statusId;
        ComponentId = componentId;
        FromDateTime = fromDateTime;
        ToDateTime = toDateTime;
    }
}

internal sealed class DateTimeRange
{
    public DateTime From { get; set; }

    public DateTime To { get; set; }

    public DateTimeRange(DateTime from, DateTime to)
    {
        From = from;
        To = to;
    }
}

答案 1 :(得分:0)

您是否要像这样分组?

var grouped = Problem_Fact_Measure_Overalls.GroupBy(r => r.ATMID)
.Select(g => new
{
    Atmid = g.Key,
    St_Comp = g.Select(r => (r.StatusId, r.ComponentId)).ToArray(),
    FromDateTime = g.Min(r => r.FromDateTime),
    ToDateTime = g.Max(r => r.ToDateTime)
});

输出:

4   (1, 3), (2, 5)           2019-01-01 18:20   2019-01-01 18:45
5   (3, 2), (1, 2), (6, 5)   2019-01-01 18:00   2019-01-01 18:50