I'm trying to create a generic function that can take a list of objects with start-datetime and end-datetime and combine them if they're right after one another with no gaps between.
public static IEnumerable<T> MakeBlocks<T>(IEnumerable<T> input)
{
List<T> outList = new List<T>();
if (input.Count() == 0) return outList;
T thisEntry = input.First();
foreach (var nextEntry in input.Skip(1))
{
if ( nextEntry != null && nextEntry.StartDT == thisEntry.EndDT)
{
thisEntry.EndDT = nextEntry.EndDT;
}
else
{
outList.Add(thisEntry);
thisEntry = nextEntry;
}
}
outList.Add(thisEntry);
return outList;
}
This works fine if I know what the "From" and "to" property is called, but how can I do this with a generic?
The "unknown" properties in the above pseudo-example are called StartDT and EndDT, but they could be called anything.
In JavaScript I can just supply the the property name as a string, but that won't do in c#.
Is this possible and how?
CodePudding user response:
You can use generic constraints. So you would have a class that all your T's inherit from. Like so:
Modified method to use generic constarint
public static IEnumerable<T> MakeBlocks<T>(IEnumerable<T> input) where T : SomeClass
{
List<T> outList = new List<T>();
if (input.Count() == 0) return outList;
T thisEntry = input.First();
foreach (var nextEntry in input.Skip(1))
{
if (nextEntry != null && nextEntry.StartDT == thisEntry.EndDT)
{
thisEntry.EndDT = nextEntry.EndDT;
}
else
{
outList.Add(thisEntry);
thisEntry = nextEntry;
}
}
outList.Add(thisEntry);
return outList;
}
Base class that all your T's should inherit from
public abstract class SomeClass
{
public DateTime EndDT { get; set; }
public DateTime StartDT { get; set; }
}
CodePudding user response:
This is possible, if you pass Getter and Setter functions:
public static IEnumerable<T> MakeBlocks<T>(IEnumerable<T> input,
Func<T, DateTime> getStartProperty,
Func<T, DateTime> getEndProperty,
Action<T, DateTime> setEndProperty) {
List<T> outList = new List<T>();
if (!input.Any()) return outList;
T thisEntry = input.First();
foreach (var nextEntry in input.Skip(1))
{
if ( nextEntry != null && getStartProperty(nextEntry) == getEndProperty(thisEntry))
{
setEndProperty(thisEntry, getEndProperty(nextEntry));
}
else
{
outList.Add(thisEntry);
thisEntry = nextEntry;
}
}
outList.Add(thisEntry);
return outList;
}
So you'd invoke it with some lambda functions:
var result = MakeBlocks<MyClass>(
myCollection,
mc => mc.StartDate,
mc => mc.EndDate,
(mc, value) => mc.EndDate = value
);