Reputation: 1443
Let's say I have the following In-Memory Collection of items and Dates:
ItemID: 1, Date: 10/10/2018 11:30 AM
ItemID: 1, Date: 10/10/2018 11:32 AM
ItemID: 1, Date: 10/10/2018 11:33 AM
ItemID: 1, Date: 10/10/2018 11:34 AM
ItemID: 1, Date: 10/10/2018 11:57 AM
ItemID: 2, Date: 10/10/2018 7:45 AM
ItemID: 2, Date: 10/10/2018 7:49 AM
ItemID: 3, Date: 10/10/2018 8:45 AM
ItemID: 3, Date: 10/10/2018 9:13 AM
I'd like to group by ItemID and Date but the dates I want them group by the ones that fall within 5 minutes of the lowest date, in this case for Item 1 teh lowest time is : 11:30 AM so group by anything that falls between the following minutes 11:30 + 5 minutes
In the data set above the following would be grouped
ItemID: 1, Date: 10/10/2018 11:30 AM <-- Grouped with Item 1
ItemID: 1, Date: 10/10/2018 11:32 AM <-- Grouped with Item 1
ItemID: 1, Date: 10/10/2018 11:33 AM <-- Grouped with Item 1
ItemID: 1, Date: 10/10/2018 11:34 AM <-- Grouped with Item 1
ItemID: 1, Date: 10/10/2018 11:57 AM <-- Not Grouped
ItemID: 2, Date: 10/10/2018 7:45 AM <-- Grouped with Item 2
ItemID: 2, Date: 10/10/2018 7:49 AM <-- Grouped with Item 2
ItemID: 3, Date: 10/10/2018 8:45 AM <-- Not Grouped
ItemID: 3, Date: 10/10/2018 9:13 AM <-- Not Grouped
The ones that are grouped are the ones that fall within 5 minutes of each other.
OUTPUT:
Item 1:
Date: 10/10/2018 11:30 AM <--- Lowest Minute is 30 so 30 + 5 range group
Date: 10/10/2018 11:32 AM
Date: 10/10/2018 11:33 AM
Date: 10/10/2018 11:34 AM
Item 1:
Date: 10/10/2018 11:57 AM
Item 2:
Date: 10/10/2018 7:45 AM
Date: 10/10/2018 7:49 AM
Item 3:
Date: 10/10/2018 8:45 AM
Item 3:
Date: 10/10/2018 9:13 AM
Upvotes: 3
Views: 137
Reputation: 2898
You can group by id first and then group each group by number of total minutes elapsed from min group time divided by interval without a reminder:
var interval = 5;
var groupedItems = items
.GroupBy(i => i.ItemId, (k, g) => g
.GroupBy(i => (long)(i.Date - g.Min(e => e.Date)).TotalMinutes / interval))
.SelectMany(g => g);
Upvotes: 5
Reputation: 29
Here a fully functional working example on how to solve this aligned with the previous two answers. Just open VS and create a new ConsoleApp and paste the stuff and try it out. Hope you find your way through this now.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp3
{
public class Item
{
public int ItemID { get; set; }
public DateTime Date { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Item> items = new List<Item>
{
new Item { ItemID= 1, Date = new DateTime(2018, 10, 10, 11, 30, 0) },
new Item { ItemID= 1, Date = new DateTime(2018, 10, 10, 11, 32, 0) },
new Item { ItemID= 1, Date = new DateTime(2018, 10, 10, 11, 33, 0) },
new Item { ItemID= 1, Date = new DateTime(2018, 10, 10, 11, 34, 0) },
new Item { ItemID= 1, Date = new DateTime(2018, 10, 10, 11, 57, 0) },
new Item { ItemID= 2, Date = new DateTime(2018, 10, 10, 7, 45, 0) },
new Item { ItemID= 2, Date = new DateTime(2018, 10, 10, 7, 49, 0) },
new Item { ItemID= 2, Date = new DateTime(2018, 10, 10, 8, 45, 0) },
new Item { ItemID= 2, Date = new DateTime(2018, 10, 10, 9, 13, 0) }
};
var groupedItems = items.GroupBy(i => i.ItemID, (k, g) => g
.GroupBy(i => (long)new TimeSpan(i.Date.Ticks - g.Min(e => e.Date).Ticks).TotalMinutes / 5));
var groupIndex = 0;
foreach (var group in groupedItems)
{
foreach (IGrouping<long, Item> groupItem in group)
{
Console.Out.WriteLine("Group " + (++groupIndex));
foreach (var item in groupItem)
{
Console.WriteLine($"Item {item.ItemID}. Date: {item.Date}");
}
Console.Out.WriteLine();
}
}
Console.ReadLine();
}
}
}
Cheers
Upvotes: 2
Reputation: 1802
Here's a very crude example I put together to get your desired "output". Of course apply it to your situation and add your own error checking / validation / ordering/ etc. I also didn't use LINQ as I didn't feel it was appropriate per your comment. Lastly, I didn't match your output exactly. I get the sense that this might be for an assignment of some sort. If so, then you surely can make that small adjustment.
Support class:
class Item
{
public int ItemID { get; set; }
public DateTime Date { get; set; }
}
Example method:
private static void Example()
{
var myList = new List<Item>
{
new Item {ItemID = 1, Date = DateTime.Parse("10/10/2018 11:30 AM") },
new Item {ItemID = 1, Date = DateTime.Parse("10/10/2018 11:32 AM") },
new Item {ItemID = 1, Date = DateTime.Parse("10/10/2018 11:33 AM") },
new Item {ItemID = 1, Date = DateTime.Parse("10/10/2018 11:34 AM") },
new Item {ItemID = 1, Date = DateTime.Parse("10/10/2018 11:57 AM") },
new Item {ItemID = 2, Date = DateTime.Parse("10/10/2018 7:45 AM") },
new Item {ItemID = 2, Date = DateTime.Parse("10/10/2018 7:49 AM") },
new Item {ItemID = 3, Date = DateTime.Parse("10/10/2018 8:45 AM") },
new Item {ItemID = 3, Date = DateTime.Parse("10/10/2018 9:13 AM") }
};
Item baseItem = myList.First();
int currentGroup = 1;
// output first group
Console.WriteLine($"Group {currentGroup}");
foreach (var item in myList) // .OrderBy(i=>i.ItemID).ThenBy(i => i.Date))
{
// if item is different OR date is >5mins from the first
if (item.ItemID != baseItem.ItemID || item.Date > baseItem.Date.AddMinutes(5))
{
// different group
currentGroup += 1;
// set new base item
baseItem = item;
Console.WriteLine($"\nGroup {currentGroup}");// output new group
}
// output the item
Console.WriteLine($"Item {item.ItemID}. Date: {item.Date}");
}
}
Output:
Group 1
Item 1. Date: 10/10/2018 11:30:00 AM
Item 1. Date: 10/10/2018 11:32:00 AM
Item 1. Date: 10/10/2018 11:33:00 AM
Item 1. Date: 10/10/2018 11:34:00 AM
Group 2
Item 1. Date: 10/10/2018 11:57:00 AM
Group 3
Item 2. Date: 10/10/2018 7:45:00 AM
Item 2. Date: 10/10/2018 7:49:00 AM
Group 4
Item 3. Date: 10/10/2018 8:45:00 AM
Group 5
Item 3. Date: 10/10/2018 9:13:00 AM
Upvotes: 2