-1

I have the following code

TimeSpan[] hours = new[] { 
  new TimeSpan(10,35,50), 
  new TimeSpan(10,36,48), 
  new TimeSpan(10,41,48), 
  new TimeSpan(10,47,58), 
  new TimeSpan(10,49,14), 
  new TimeSpan(11,22,15), 
  new TimeSpan(11,24,18), 
  new TimeSpan(11,25,25), 
};

I want to group the hours and minutes by 5 minutes. I want to get the result below

1st Group

**10:35:50
10:36:48
10:41:48** 

2nd Group

**10:47:58
10:49:14** 

3rd Group

**11:22:15
11:24:18
11:25:25**

I have tried the following code but I cannot get the exact result that I need

  var query = (from x in hours select x)
    .GroupBy(r=> r.Ticks / TimeSpan.FromMinutes(5).Ticks));
Draken
  • 3,134
  • 13
  • 34
  • 54
Sercan
  • 13
  • 1
  • Possible duplicate of [LINQ aggregate and group by periods of time](https://stackoverflow.com/questions/8856266/linq-aggregate-and-group-by-periods-of-time) – evgiv0 Oct 17 '19 at 07:44
  • 3
    I'm afraid I don't understand. You want to group by _5 minutes_, but your first group spans a period of 5 minutes and 58 seconds. How is this grouping meant to occur? – Martin Oct 17 '19 at 07:44
  • @evgiv0 itried this but i can not get the result – Sercan Oct 17 '19 at 07:45
  • @Martin first group follows each other. **10:35:50 10:36:48 10:41:48** 10:36:48 and 10:41:48 should be in same group..it is sequential – Sercan Oct 17 '19 at 07:46
  • 2
    @Sercan still doesn't make sense. What do you mean by *sequential*. That first group is still *more than 5 minutes*. The code you have already [does group correctly by 5 minutes](https://dotnetfiddle.net/zXhw0g). – Jamiec Oct 17 '19 at 07:49
  • Wait, do you want to group any sequence, where two consecutive elements are closer than 5 minutes? – Fildor Oct 17 '19 at 07:51
  • 1
    oooh that would make more sense. Well done for being able to interpret odd requirements @Fildor – Jamiec Oct 17 '19 at 07:52
  • @Jamiec 10:35:50 and 10:36:48 there is around 1 minutes different. 10:36:48 and 10:41:48 there is minutes difference.I want to put them in one group.Because 10:36:48 follows 10:41:48 and it follows 5 min sequent – Sercan Oct 17 '19 at 08:02
  • @Sercan yep we got you now thanks to Fildor's comment. See answers either should work for you. It's worth remembering to put this sort of detail in the original question in future. – Jamiec Oct 17 '19 at 08:05

4 Answers4

0

You can try to implement a simple loop instead of Linq:

Code:

private static IEnumerable<TimeSpan[]> MyGrouping(IEnumerable<TimeSpan> source, 
                                                  double minutes) {
  List<TimeSpan> list = new List<TimeSpan>();

  foreach (TimeSpan item in source.OrderBy(x => x)) {
    // shall we start a new group?
    if (list.Any() && (item - list.Last()).TotalMinutes > minutes) {
      // if yes, return the previous 
      yield return list.ToArray();

      list.Clear();
    }

    list.Add(item);
  }

  if (list.Any())
    yield return list.ToArray();
}

Demo:

var result = MyGrouping(hours, 5)
  .Select((list, index) => $"Group {index + 1}: [{string.Join(", ", list)}]");

Console.Write(string.Join(Environment.NewLine, result));

Outcome:

Group 1: [10:35:50, 10:36:48, 10:41:48]
Group 2: [10:47:58, 10:49:14]
Group 3: [11:22:15, 11:24:18, 11:25:25]
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
0

You could do this by using Aggregate to determine which "group" each timespan would belong to and then GroupBy that group

TimeSpan[] hours = new[] { 
        new TimeSpan(10,35,50), 
        new TimeSpan(10,36,48), 
        new TimeSpan(10,41,48), 
        new TimeSpan(10,47,58), 
        new TimeSpan(10,49,14), 
        new TimeSpan(11,22,15), 
        new TimeSpan(11,24,18), 
        new TimeSpan(11,25,25), 
    };
    var query = hours.Aggregate(new List<(int group, TimeSpan ts)>(),  (acc,curr) => {
        if(acc.Count == 0)
            acc.Add((0,curr));          
        else 
        {
            var (lastGroup,lastTs) = acc.Last();
            if(curr.Subtract(lastTs).TotalMinutes <= 5)
                acc.Add((lastGroup,curr));
            else
                acc.Add((lastGroup+1,curr));
        }
        return acc;
    }).GroupBy(x => x.group, y => y.ts);
    foreach(var item in query)
        Console.WriteLine(String.Join(", ", item));

Output is

10:35:50, 10:36:48, 10:41:48
10:47:58, 10:49:14
11:22:15, 11:24:18, 11:25:25

Live example: https://dotnetfiddle.net/zXhw0g

Good question here on Aggregate explained to help you understand it.

Jamiec
  • 133,658
  • 13
  • 134
  • 193
0

You can write a method to group the items like so:

public static IEnumerable<List<TimeSpan>> GroupItemsWithin(IEnumerable<TimeSpan> times, TimeSpan maxDelta)
{
    var previous = TimeSpan.MinValue;
    var spans    = new List<TimeSpan>();

    foreach (var span in times)
    {
        if (previous == TimeSpan.MinValue || (span - previous) <= maxDelta)
        {
            spans.Add(span);
        }
        else if (spans.Count > 0)
        {
            yield return spans;
            spans = new List<TimeSpan>{ span };
        }

        previous = span;
    }

    if (spans.Count > 0)
        yield return spans;
}

Then you can use it like this:

public static void Main()
{
    TimeSpan[] hours = new[] {
        new TimeSpan(10, 35, 50),
        new TimeSpan(10, 36, 48),
        new TimeSpan(10, 41, 48),
        new TimeSpan(10, 47, 58),
        new TimeSpan(10, 49, 14),
        new TimeSpan(11, 22, 15),
        new TimeSpan(11, 24, 18),
        new TimeSpan(11, 25, 25),
    };

    foreach (var group in GroupItemsWithin(hours, TimeSpan.FromMinutes(5)))
    {
        Console.WriteLine(string.Join(", ", group));
    }
}

Output:

10:35:50, 10:36:48, 10:41:48

10:47:58, 10:49:14

11:22:15, 11:24:18, 11:25:25

Community
  • 1
  • 1
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
0

If you want to use Linq. Then this is the simplest solution Demo

      TimeSpan[] hours = new[] 
      { new TimeSpan(10, 35, 50),
        new TimeSpan(10, 36, 48),
        new TimeSpan(10, 41, 48),
        new TimeSpan(10, 47, 58),
        new TimeSpan(10, 49, 14),
        new TimeSpan(11, 22, 15),
        new TimeSpan(11, 24, 18),
        new TimeSpan(11, 25, 25), };
      int groupID = -1;
      var result = hours.OrderBy(h => h).Select((item, index) =>
        {
          if (index == 0 || (!(Math.Abs(hours[index - 1].Subtract(item).TotalMinutes) <= 5)))
            ++groupID;

          return new { group = groupID, item = item };
        }).GroupBy(item => item.group);
Ihtsham Minhas
  • 1,415
  • 1
  • 19
  • 31