karim harfakti
karim harfakti

Reputation: 13

Linq Groupby Elapsed Time

I need to group by a list by owner, town and "timeout". I will explain "timeout":

public class Ticket
{
    public int pk { get; set; }
    public string owner { get; set; }
    public string reffering { get; set; }
    public DateTime? created_time { get; set; }
}

And I got a List. I want to create a List> where each sublist contains a list of tickets where created_time is less than 10 seconds.

Here is a sample list of tickets :

pk   owner   reffering   created_time
#1   John    Sam         15/11/2017 11:33:20
#2   John    Sam         15/11/2017 11:33:21
#3   Pat     Jerry       15/11/2017 11:33:27
#4   John    Sam         15/11/2017 11:33:28
#6   Pat     Jerry       15/11/2017 11:33:35
#5   John    Sam         15/11/2017 11:34:00

And I need to get a list of list with

   pk   owner   reffering   created_time
---#1 Sub List ---
    #1   John    Sam         15/11/2017 11:33:20
    #2   John    Sam         15/11/2017 11:33:21
    #4   John    Sam         15/11/2017 11:33:28
---#2 Sub List ---
    #5   John    Sam         15/11/2017 11:34:00
---#3 Sub List ---
    #3   Pat     Jerry       15/11/2017 11:33:27
    #6   Pat     Jerry       15/11/2017 11:33:35

Here my starting code but I can find out how to do this..

List<List<Ticket>> result = tickets.OrderBy(p => p.created_time).GroupBy(p => new { p.owner, p.reffering }).Select(g => g.ToList()).ToList();

Hope some can help especially with the elapsed time.. it makes me crazy!

Upvotes: 1

Views: 108

Answers (2)

Edward N
Edward N

Reputation: 997

I'm not quite sure you can do all of this with LINQ, here is my implementation as your references

var ticketGroups = tickets.OrderBy(x => x.created_time)
                            .GroupBy(x => new { x.owner, x.reffering });
var tensecondTimespam = new TimeSpan(0, 0, 10);
var results = new List<List<Ticket>>();
var ticketTemps = new List<Ticket>();

foreach (var ticketGroup in ticketGroups)
{
    foreach (var ticket in ticketGroup)
    {
        var currentTicket = ticketTemps.LastOrDefault();
        var isWithinTimeRange = currentTicket == null
                                || ticket.created_time.Value - currentTicket.created_time.Value <= tensecondTimespam;

        if (!isWithinTimeRange)
        {
            results.Add(ticketTemps);
            ticketTemps = new List<Ticket>();
        }
        ticketTemps.Add(ticket);
    }
    if (ticketTemps.Any())
    {
        results.Add(ticketTemps);
        ticketTemps = new List<Ticket>();
    }
}

The result variable will hold your expected output

Upvotes: 0

Vincent
Vincent

Reputation: 3746

Here is something that could help. It is grouping the entries by fixed buckets of 10 seconds.

void Main()
{
    var ticketsList = new List<Ticket>();
    ticketsList.Add(new Ticket { pk = 1, owner="John", reffering="Sam"  , created_time = new DateTime(2017, 11, 15, 11, 33, 20) });
    ticketsList.Add(new Ticket { pk = 2, owner="John", reffering="Sam"  , created_time = new DateTime(2017, 11, 15, 11, 33, 21) });
    ticketsList.Add(new Ticket { pk = 3, owner="Pat" , reffering="Jerry", created_time = new DateTime(2017, 11, 15, 11, 33, 27) });
    ticketsList.Add(new Ticket { pk = 4, owner="John", reffering="Sam"  , created_time = new DateTime(2017, 11, 15, 11, 33, 28) });
    ticketsList.Add(new Ticket { pk = 6, owner="Pat" , reffering="Jerry", created_time = new DateTime(2017, 11, 15, 11, 33, 35) });
    ticketsList.Add(new Ticket { pk = 5, owner="John", reffering="Sam"  , created_time = new DateTime(2017, 11, 15, 11, 34, 00) });

    var now = DateTime.Now;


    var orderedList = ticketsList.OrderBy(p => p.created_time).GroupBy(p => new { p.owner, p.reffering }).Select(g => g.ToList());

    // Here is the 10 seconds grouping. I'm basically creating a new date 
    // starting at the beginning of a 10 seconds interval. I will then 
    // use this new date to perform the grouping.
    var normalizedGroupKeysList = ticketsList.Select(t => new { ticket = t, groupKey = t.created_time.HasValue ? t.created_time.Value.AddSeconds(-t.created_time.Value.Second % 10) : now });

    var result = normalizedGroupKeysList.GroupBy(t => t.groupKey, t => t.ticket);
}

public class Ticket
{
    public int pk { get; set; }
    public string owner { get; set; }
    public string reffering { get; set; }

    public DateTime? created_time { get; set; }
}

The output is:

Key=    11/15/2017 11:33:20 AM
pk  owner   reffering   created_time
1   John    Sam 11/15/2017 11:33:20 AM
2   John    Sam 11/15/2017 11:33:21 AM
3   Pat Jerry   11/15/2017 11:33:27 AM
4   John    Sam 11/15/2017 11:33:28 AM      

Key=    11/15/2017 11:33:30 AM
pk  owner   reffering   created_time
6   Pat Jerry   11/15/2017 11:33:35 AM

Key=    11/15/2017 11:34:00 AM
pk  owner   reffering   created_time
5   John    Sam 11/15/2017 11:34:00 AM

If you want to dynamically group your tickets dynamically by group of 10 seconds in dynamic buckets, you should need some clustering algorithm to find the best partition of your data.

Upvotes: 1

Related Questions