「これを達成するための最も効率的な方法は何ですか?」
リストがソートされていると言うので、ソース時間の 1 回のパスで必要な時間を生成できます。
これにより、他のすべての回答によって行われるグループ化が回避されます。入力データがソートされていない場合は、このようなグループ化が必要になりますが、日付/時刻の昇順でソートされているため、明示的にグループ化する必要はありません。
まず、文字列を に変換する単純なコンバーターを定義しましょうDateTime
。
public IEnumerable<DateTime> ParseTimes(IEnumerable<string> times)
{
return times.Select(time => DateTime.ParseExact(time, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
}
次に、次のように、毎日の最初と最後の時間 (または、その日に 1 回しか指定されていない場合はその日の唯一の時間) を返す単純なメソッドを作成できます。
public IEnumerable<DateTime> FirstAndLastTimesForEachDay(IEnumerable<DateTime> times)
{
DateTime previous = DateTime.MinValue;
DateTime current = DateTime.MinValue;
foreach (var time in times)
{
if (previous.Date < time.Date)
{
if (previous != current)
yield return previous;
yield return time;
current = time;
}
previous = time;
}
if (previous != current)
yield return previous;
}
次に、次のように使用できます。
var times = new []
{
"2013-05-02 07:45:15",
"2013-05-02 09:25:01",
"2013-05-02 18:15:15",
"2013-05-03 12:34:45",
"2013-05-04 08:45:15",
"2013-05-04 17:45:35",
"2013-05-05 20:00:00"
};
foreach (var time in FirstAndLastTimesForEachDay(ParseTimes(times)))
Console.WriteLine(time);
上記の実装では、DateTime が 1 つしか含まれていない日には、DateTime が 1 つしか出力されないことに注意してください。代わりに、1 日に発生する 1 つの DateTime の 2 倍を出力に含める場合 (つまり、時刻が同じであっても、1 日あたりの DateTime のペアは常に存在します)、実装を次のように変更します。
public IEnumerable<DateTime> FirstAndLastTimesForEachDay(IEnumerable<DateTime> times)
{
DateTime previous = DateTime.MinValue;
foreach (var time in times)
{
if (previous.Date < time.Date)
{
if (previous != DateTime.MinValue)
yield return previous;
yield return time;
}
previous = time;
}
if (previous != DateTime.MinValue)
yield return previous;
}