I have a list of tasks and subtasks (using a parent-child relationship) that have been created with sequential dates from a template. I need to identify the parent for each task according to the template parent Id and where the due date falls in respect of the parent due date. Confused? Let me explain by example. Here is my class:
public class Task
{
public int Id { get; set; }
public int? ParentId { get; set; }
public int TemplateId { get; set; }
public int? TemplateParentId { get; set; }
public DateTime DueDate { get; set; }
}
Here is some data to make a bit more sense of it:
List<Task> tasks = new List<Task>( );
tasks.Add( new Task { Id = 1, ParentId = null, TemplateId = 1, TemplateParentId = null, DueDate = new DateTime( 2020, 12, 31 ) } );
tasks.Add( new Task { Id = 2, ParentId = null, TemplateId = 1, TemplateParentId = null, DueDate = new DateTime( 2021, 12, 31 ) } );
tasks.Add( new Task { Id = 3, ParentId = null, TemplateId = 2, TemplateParentId = 1, DueDate = new DateTime( 2020, 6, 1 ) } );
tasks.Add( new Task { Id = 4, ParentId = null, TemplateId = 2, TemplateParentId = 1, DueDate = new DateTime( 2021, 6, 1 ) } );
tasks.Add( new Task { Id = 5, ParentId = null, TemplateId = 2, TemplateParentId = 1, DueDate = new DateTime( 2021, 12, 31 ) } );
tasks.Add( new Task { Id = 6, ParentId = null, TemplateId = 3, TemplateParentId = null, DueDate = new DateTime( 2020, 10, 31 ) } );
tasks.Add( new Task { Id = 7, ParentId = null, TemplateId = 4, TemplateParentId = 3, DueDate = new DateTime( 2020, 10, 31 ) } );
3 of the tasks are parent tasks and 4 are subtasks according to the TemplateParentId value. Notice that the ParentId field is blank for all tasks? This is the value I intend to find using the following rules:
I only need to update the tasks that have a parent. All top-level parents (as represented by TemplateParentId == null
) are fine.
I know the TemplateParentId
of a given subtask and I know the DueDate
. The due date needs to be before the next due date of a parent. For example, if the subtask's TemplateParentId == 1
then it's parents have TemplateId == 1
and it's parent will be the one with the closest future or same date.
Rows with the same TemplateId will never have the same date. They are always different by way of sequential dates.
For example using the sample data, the top 2 rows have the same TemplateId
, but they have due dates a year apart. Basically, it's the same task that needs to be done every year. Rows 3, 4 and 5 are subtasks that have the same template parent, but they have different due dates. These tasks need to be completed before or on the same day their parent is due. For example, task 3 is due in June 2020 and out of it's 2 possible parents the next closest date is December 2020. So, it's ParentId
should be 1. Task 5 is due to be completed on 31 Dec 2021 and the next closest parent due date is the same date, so it's ParentId
should be 2.
The list is in no particular order.
Hopefully that makes sense. I am not sure how to code this, but here is my untested mostly pseudo code:
foreach( var task in tasks.OrderBy( t => t.DueDate ) )
{
if ( task.TemplateParentId == null || task.ParentId.HasValue )
{
continue;
}
var parentId = tasks.Where( t => t.TemplateId == task.TemplateParentId && t.DueDate >= task.DueDate ).Select( t => t.Id ).LastOrDefault( );
if( parentId > 0 )
{
// update the row with the ParentId, but this is not possible because it will alter the collection and an exception will ensue!
}
}
How can I even do this and with efficiency? Thanks in advance.
And this highlights the value of Stackoverflow. Whilst composing the question, I inadvertently come up with a solution that appears to be efficient:
foreach( var task in tasks.OrderBy( t => t.DueDate ) )
{
if ( task.TemplateParentId == null || task.ParentId.HasValue )
{
continue;
}
var parentId = tasks.Where( t => t.TemplateId == task.TemplateParentId && t.DueDate >= task.DueDate ).Select( t => t.Id ).FirstOrDefault( );
if( parentId > 0 )
{
// store the value in a dictionary and process the updates afterwards
}
}